@effekt-lang/effekt 0.30.0 → 0.31.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/bin/effekt CHANGED
Binary file
@@ -1,7 +1,10 @@
1
1
  /// Warning: This library currently only works with ASCII characters, **not** unicode!
2
2
  module char
3
3
 
4
+ import effekt
4
5
  import exception
6
+ import option
7
+ import result
5
8
 
6
9
 
7
10
  /// Checks if the given character is an ASCII whitespace
@@ -15,56 +18,51 @@ def isWhitespace(c: Char): Bool = c match {
15
18
  case _ => false
16
19
  }
17
20
 
18
- /// Gets the value of a given ASCII digit in base 10
19
- def digitValue(char: Char): Option[Int] =
20
- // TODO use fail
21
+ /// Gets the value of a given ASCII digit in base 10,
22
+ /// throwing an exception on wrong format
23
+ def digitValue(char: Char): Int / Exception[WrongFormat] =
21
24
  if (char >= '0' && char <= '9') {
22
- Some(char.toInt - '0'.toInt)
25
+ char.toInt - '0'.toInt
23
26
  } else {
24
- None()
27
+ wrongFormat("Not a valid digit: '" ++ char.toString ++ "' in base 10")
25
28
  }
26
29
 
27
- /// Gets the value of a given ASCII digit in base 16
28
- def hexDigitValue(char: Char): Option[Int] =
29
- // TODO use fail
30
+ /// Gets the value of a given ASCII digit in base 16,
31
+ /// throwing an exception on wrong format
32
+ def hexDigitValue(char: Char): Int / Exception[WrongFormat] =
30
33
  char match {
31
- case char and char >= '0' && char <= '9' => Some(char.toInt - '0'.toInt)
32
- case char and char >= 'A' && char <= 'F' => Some((char.toInt - 'A'.toInt) + 10)
33
- case char and char >= 'a' && char <= 'f' => Some((char.toInt - 'a'.toInt) + 10)
34
- case _ => None()
34
+ case char and char >= '0' && char <= '9' => char.toInt - '0'.toInt
35
+ case char and char >= 'A' && char <= 'F' => (char.toInt - 'A'.toInt) + 10
36
+ case char and char >= 'a' && char <= 'f' => (char.toInt - 'a'.toInt) + 10
37
+ case _ => wrongFormat("Not a valid digit: '" ++ char.toString ++ "' in base 16")
35
38
  }
36
39
 
37
- /// Gets the value of a given ASCII digit in the given base up to 36
38
- def digitValue(char: Char, base: Int): Option[Int] = {
39
- // TODO use fail
40
- val perhapsDigit = char match {
41
- case char and char >= '0' && char <= '9' => Some(char.toInt - '0'.toInt)
42
- case char and char >= 'A' && char <= 'Z' => Some((char.toInt - 'A'.toInt) + 10)
43
- case char and char >= 'a' && char <= 'z' => Some((char.toInt - 'a'.toInt) + 10)
44
- case _ => None()
45
- }
46
- perhapsDigit match {
47
- case Some(digit) =>
48
- if (digit < base) {
49
- Some(digit)
50
- } else {
51
- None()
52
- }
53
- case None() => None()
40
+ /// Gets the value of a given ASCII digit in the given base up to 36,
41
+ /// throwing an exception on wrong format
42
+ def digitValue(char: Char, base: Int): Int / Exception[WrongFormat] = {
43
+ val digit = char match {
44
+ case char and char >= '0' && char <= '9' => char.toInt - '0'.toInt
45
+ case char and char >= 'A' && char <= 'Z' => (char.toInt - 'A'.toInt) + 10
46
+ case char and char >= 'a' && char <= 'z' => (char.toInt - 'a'.toInt) + 10
47
+ case _ => wrongFormat("Not a valid digit: '" ++ char.toString ++ "'")
54
48
  }
49
+ if (digit >= base) {
50
+ wrongFormat("Digit '" ++ digit.show ++ "' is too big for base " ++ base.show)
51
+ }
52
+ digit
55
53
  }
56
54
 
57
55
  /// Checks if the given character is an ASCII digit in base 10
58
- /// Use `digitValue(c: Char)` to get the numeric value out.
59
- def isDigit(char: Char): Bool = digitValue(char).isDefined
56
+ /// Prefer using `digitValue(c: Char)` to get the numeric value out.
57
+ def isDigit(char: Char): Bool = result[Int, WrongFormat] { digitValue(char) }.isSuccess
60
58
 
61
59
  /// Checks if the given character is an ASCII digit in base 16
62
- /// Use `hexDigitValue(c: Char)` to get the numeric value out.
63
- def isHexDigit(char: Char): Bool = hexDigitValue(char).isDefined
60
+ /// Prefer using `hexDigitValue(c: Char)` to get the numeric value out.
61
+ def isHexDigit(char: Char): Bool = result[Int, WrongFormat] { hexDigitValue(char)}.isSuccess
64
62
 
65
63
  /// Checks if the given character is an ASCII digit in base 10
66
- /// Use `digitValue(c: Char)` to get the numeric value out.
67
- def isDigit(char: Char, base: Int): Bool = digitValue(char, base).isDefined
64
+ /// Prefer using `digitValue(c: Char, base: Int)` to get the numeric value out.
65
+ def isDigit(char: Char, base: Int): Bool = result[Int, WrongFormat] { digitValue(char, base) }.isSuccess
68
66
 
69
67
  /// Checks if a given character is a 7-bit ASCII character
70
68
  def isASCII(c: Char): Bool = { c.toInt < 128 }
@@ -80,3 +78,100 @@ def isAlphanumeric(c: Char): Bool = isDigit(c) || isLower(c) || isUpper(c)
80
78
 
81
79
  /// Checks if a given character is an ASCII alphabetic character
82
80
  def isAlphabetic(c: Char): Bool = isLower(c) || isUpper(c)
81
+
82
+ // Characters
83
+ // ----------
84
+ //
85
+ // JS: Int (Unicode codepoints)
86
+ // Chez: ?
87
+ // LLVM: i64 representing utf-8 (varying length 1-4 bytes)
88
+
89
+ extern pure def toString(ch: Char): String =
90
+ js "String.fromCodePoint(${ch})"
91
+ chez "(string (integer->char ${ch}))"
92
+ llvm """
93
+ %z = call %Pos @c_bytearray_show_Char(%Int ${ch})
94
+ ret %Pos %z
95
+ """
96
+
97
+ // Since we currently represent Char by integers in all backends, we could reuse comparison
98
+ extern pure def toInt(ch: Char): Int =
99
+ js "${ch}"
100
+ chez "${ch}"
101
+ llvm "ret %Int ${ch}"
102
+ vm "string::toInt(Char)"
103
+
104
+ extern pure def toChar(codepoint: Int): Char =
105
+ js "${codepoint}"
106
+ chez "${codepoint}"
107
+ llvm "ret %Int ${codepoint}"
108
+ vm "string::toChar(Int)"
109
+
110
+
111
+ extern pure def infixLt(x: Char, y: Char): Bool =
112
+ js "(${x} < ${y})"
113
+ chez "(< ${x} ${y})"
114
+ llvm """
115
+ %z = icmp slt %Int ${x}, ${y}
116
+ %fat_z = zext i1 %z to i64
117
+ %adt_boolean = insertvalue %Pos zeroinitializer, i64 %fat_z, 0
118
+ ret %Pos %adt_boolean
119
+ """
120
+ vm "string::infixLt(Char, Char)"
121
+
122
+ extern pure def infixLte(x: Char, y: Char): Bool =
123
+ js "(${x} <= ${y})"
124
+ chez "(<= ${x} ${y})"
125
+ llvm """
126
+ %z = icmp sle %Int ${x}, ${y}
127
+ %fat_z = zext i1 %z to i64
128
+ %adt_boolean = insertvalue %Pos zeroinitializer, i64 %fat_z, 0
129
+ ret %Pos %adt_boolean
130
+ """
131
+ vm "string::infixLte(Char, Char)"
132
+
133
+ extern pure def infixGt(x: Char, y: Char): Bool =
134
+ js "(${x} > ${y})"
135
+ chez "(> ${x} ${y})"
136
+ llvm """
137
+ %z = icmp sgt %Int ${x}, ${y}
138
+ %fat_z = zext i1 %z to i64
139
+ %adt_boolean = insertvalue %Pos zeroinitializer, i64 %fat_z, 0
140
+ ret %Pos %adt_boolean
141
+ """
142
+ vm "string::infixGt(Char, Char)"
143
+
144
+ extern pure def infixGte(x: Char, y: Char): Bool =
145
+ js "(${x} >= ${y})"
146
+ chez "(>= ${x} ${y})"
147
+ llvm """
148
+ %z = icmp sge %Int ${x}, ${y}
149
+ %fat_z = zext i1 %z to i64
150
+ %adt_boolean = insertvalue %Pos zeroinitializer, i64 %fat_z, 0
151
+ ret %Pos %adt_boolean
152
+ """
153
+ vm "string::infixGte(Char, Char)"
154
+
155
+
156
+ /**
157
+ * Determines the number of bytes needed by a codepoint
158
+ *
159
+ * Also see: https://en.wikipedia.org/wiki/UTF-8
160
+ */
161
+ def utf8ByteCount(codepoint: Char): Int = codepoint match {
162
+ case c and c >= \u0000 and c <= \u007F => 1
163
+ case c and c >= \u0080 and c <= \u07FF => 2
164
+ case c and c >= \u0800 and c <= \uFFFF => 3
165
+ case c and c >= \u10000 and c <= \u10FFFF => 4
166
+ case c => panic("Not a valid code point")
167
+ }
168
+
169
+ def utf16UnitCount(codepoint: Char): Int = codepoint match {
170
+ case c and c >= \u0000 and c <= \uFFFF => 1
171
+ case c and c >= \u10000 and c <= \u10FFFF => 4
172
+ case c => panic("Not a valid code point")
173
+ }
174
+
175
+ extern pure def charWidth(c: Char): Int =
176
+ // JavaScript strings are UTF-16 where every unicode character after 0xffff takes two units
177
+ js "(${c} > 0xffff) ? 2 : 1"
@@ -42,11 +42,32 @@ extern def println(value: String): Unit =
42
42
  js "$effekt.println(${value})"
43
43
  chez "(println_impl ${value})"
44
44
  llvm """
45
- call void @c_io_println_String(%Pos ${value})
45
+ call void @c_io_println(%Pos ${value})
46
46
  ret %Pos zeroinitializer ; Unit
47
47
  """
48
48
  vm "effekt::println(String)"
49
49
 
50
+ extern jsNode """
51
+ $effekt.readln = function readln$impl(callback) {
52
+ const readline = require('node:readline').createInterface({
53
+ input: process.stdin,
54
+ output: process.stdout,
55
+ });
56
+ readline.question('', (answer) => {
57
+ readline.close();
58
+ callback(answer);
59
+ });
60
+ }
61
+ """
62
+
63
+ extern async def readln(): String =
64
+ jsNode "$effekt.capture(callback => $effekt.readln(callback))"
65
+ llvm """
66
+ %result = call %Pos @c_io_readln()
67
+ tail call void @resume_Pos(%Stack %stack, %Pos %result)
68
+ ret void
69
+ """
70
+
50
71
  def println(value: Int): Unit = println(value.show)
51
72
  def println(value: Unit): Unit = println(value.show)
52
73
  def println(value: Double): Unit = println(value.show)
@@ -736,6 +757,9 @@ def repeat(n: Int) { action: () => Unit } = each(0, n) { n => action() }
736
757
 
737
758
  def repeat(n: Int) { action: () {Control} => Unit } = each(0, n) { (n) {l} => action() {l} }
738
759
 
760
+ // NOTE: This is emitted by the close hole code action: do not remove unless you also adjust the code action
761
+ /// Scopes a local computation
762
+ def locally[R] { p: => R } : R = p()
739
763
 
740
764
  // Splices
741
765
  // =======
@@ -54,7 +54,7 @@ extern io def panic(msg: String): Nothing =
54
54
  js "(function() { throw ${msg} })()"
55
55
  chez "(raise ${msg})"
56
56
  llvm """
57
- call void @c_io_println_String(%Pos ${msg})
57
+ call void @c_io_println(%Pos ${msg})
58
58
  call void @exit(i32 1)
59
59
  ret %Pos zeroinitializer ; Unit
60
60
  """
@@ -1,6 +1,5 @@
1
1
  module json
2
2
 
3
- import char
4
3
  import scanner
5
4
  import stream
6
5
 
@@ -390,4 +389,4 @@ namespace test {
390
389
  resume(println(e))
391
390
  }
392
391
  }
393
- }
392
+ }
@@ -35,3 +35,9 @@ def toOption[A, E](r: Result[A, E]): Option[A] = r match {
35
35
  case Success(a) => Some(a)
36
36
  case Error(exc, msg) => None()
37
37
  }
38
+
39
+ def isError[A, E](r: Result[A, E]): Bool =
40
+ if (r is Error(_, _)) true else false
41
+
42
+ def isSuccess[A, E](r: Result[A, E]): Bool =
43
+ if (r is Success(_)) true else false
@@ -1,6 +1,5 @@
1
1
  module scanner
2
2
 
3
- import char
4
3
  import stream
5
4
 
6
5
  interface Scan[A] {
@@ -49,6 +48,15 @@ def readSome[A, B] { convert: A => Option[B] }: B / { Scan[A], stop } =
49
48
  do stop()
50
49
  }
51
50
 
51
+ /// like `readSome`, but for functions `A => B / Exception[WrongFormat]` such as `char::digitValue`
52
+ def tryRead[A, B] { convert: A => B / Exception[WrongFormat] }: B / { Scan[A], stop } = {
53
+ with on[WrongFormat].default { do stop() }
54
+
55
+ val t = convert(do peek())
56
+ do skip[A]()
57
+ return t
58
+ }
59
+
52
60
  /// Reads until the predicate does not hold for the next token.
53
61
  /// Emits the tokens read.
54
62
  def readWhile[A] { predicate: A => Bool }: Unit / { Scan[A], emit[A] } =
@@ -78,7 +86,7 @@ def readString(string: String): Unit / { Scan[Char], stop } =
78
86
 
79
87
  /// Check that the next character is a digit in base 10, and if so read and return it.
80
88
  def readDigit(): Int / { Scan[Char], stop } =
81
- readSome[Char, Int]{ char => digitValue(char) }
89
+ tryRead[Char, Int]{ char => digitValue(char) }
82
90
 
83
91
  /// Read a positive decimal number.
84
92
  def readDecimal(): Int / Scan[Char] = {
@@ -91,7 +99,7 @@ def readDecimal(): Int / Scan[Char] = {
91
99
 
92
100
  /// Check that the next character is a digit in base 16, and if so read and return it.
93
101
  def readHexDigit(): Int / { Scan[Char], stop } =
94
- readSome[Char, Int]{ char => hexDigitValue(char) }
102
+ tryRead[Char, Int]{ char => hexDigitValue(char) }
95
103
 
96
104
  /// Read a hexadecimal number.
97
105
  def readHexadecimal(): Int / Scan[Char] = {
@@ -5,6 +5,7 @@ import option
5
5
  import list
6
6
  import exception
7
7
  import result
8
+ import char
8
9
 
9
10
  // TODO
10
11
  // - [ ] handle unicode codepoints (that can span two indices) correctly
@@ -61,12 +62,22 @@ def endsWith(str: String, suffix: String): Bool =
61
62
  /// TODO use a more efficient way of appending strings like a buffer
62
63
  def repeat(str: String, n: Int): String = {
63
64
  def go(n: Int, result: String): String = {
64
- if (n == 0) result
65
+ if (n <= 0) result
65
66
  else go(n - 1, result ++ str)
66
67
  }
67
68
  go(n, "")
68
69
  }
69
70
 
71
+ /// Left-pad the given string with spaces
72
+ def padLeft(str: String, n: Int): String = {
73
+ " ".repeat(n - str.length) ++ str
74
+ }
75
+
76
+ /// Right-pad the given string with spaces
77
+ def padRight(str: String, n: Int): String = {
78
+ str ++ " ".repeat(n - str.length)
79
+ }
80
+
70
81
  // TODO use .split() in JS
71
82
  def split(str: String, sep: String): List[String] = {
72
83
  val strLength = str.length
@@ -113,20 +124,18 @@ def toBool(s: String): Bool / Exception[WrongFormat] = s match {
113
124
 
114
125
  // TODO optimize (right now this will be horribly slow (compared to the native JS version)
115
126
  def toInt(str: String): Int / Exception[WrongFormat] = {
116
-
117
- val zero = '0'.toInt
118
-
119
127
  def go(index: Int, acc: Int): Int = {
120
- result[Char, OutOfBounds] { str.charAt(index) } match {
121
- case Success(c) and c >= '0' and c <= '9' =>
122
- go(index + 1, 10 * acc + (c.toInt - zero))
123
- case Success(c) => wrongFormat("Not a valid number: '" ++ str ++ "'")
124
- // wrong index means we are done parsing
125
- case Error(_, _) => acc
126
- }
128
+ // wrong index means we're done parsing
129
+ with on[OutOfBounds].default { () => acc };
130
+
131
+ val c = str.charAt(index)
132
+ val d = char::digitValue(c)
133
+ go(index + 1, 10 * acc + d)
127
134
  }
128
135
 
129
- with default[OutOfBounds, Int] { wrongFormat("Empty string is not a valid number") };
136
+ with on[OutOfBounds].default {
137
+ wrongFormat("Empty string is not a valid number")
138
+ }
130
139
 
131
140
  str.charAt(0) match {
132
141
  case '-' => 0 - go(1, 0)
@@ -136,38 +145,22 @@ def toInt(str: String): Int / Exception[WrongFormat] = {
136
145
 
137
146
  // TODO optimize (right now this will be horribly slow (compared to the native JS version)
138
147
  def toInt(str: String, base: Int): Int / Exception[WrongFormat] = {
139
-
140
- if( base > 36 || base < 1 ) {
148
+ if (base > 36 || base < 1) {
141
149
  wrongFormat("Invalid base: " ++ base.show)
142
150
  }
143
151
 
144
- val zero = '0'.toInt
145
- val l_a = 'a'.toInt
146
- val u_a = 'A'.toInt
147
-
148
- def parseDigit(c: Char): Option[Int] = {
149
- if( c >= '0' and c <= '9' and c.toInt - zero < base ) {
150
- Some(c.toInt - zero)
151
- } else if( c >= 'a' and c <= 'z' and c.toInt - l_a < base - 10 ) {
152
- Some(c.toInt - l_a + 10)
153
- } else if( c >= 'A' and c <= 'Z' and c.toInt - u_a < base - 10) {
154
- Some(c.toInt - u_a + 10)
155
- } else {
156
- None()
157
- }
158
- }
159
-
160
152
  def go(index: Int, acc: Int): Int = {
161
- result[Char, OutOfBounds] { str.charAt(index) } match {
162
- case Success(c) and parseDigit(c) is Some(d) =>
163
- go(index + 1, base * acc + d)
164
- case Success(c) => wrongFormat("Not a valid number: '" ++ str ++ "'")
165
- // wrong index means we are done parsing
166
- case Error(_, _) => acc
167
- }
153
+ // wrong index means we're done parsing
154
+ with on[OutOfBounds].default { () => acc };
155
+
156
+ val c = str.charAt(index)
157
+ val d = char::digitValue(c, base)
158
+ go(index + 1, base * acc + d)
168
159
  }
169
160
 
170
- with default[OutOfBounds, Int] { wrongFormat("Empty string is not a valid number") };
161
+ with on[OutOfBounds].default {
162
+ wrongFormat("Empty string is not a valid number")
163
+ }
171
164
 
172
165
  str.charAt(0) match {
173
166
  case '-' => 0 - go(1, 0)
@@ -222,104 +215,6 @@ def lastIndexOf(str: String, sub: String, from: Int): Option[Int] = {
222
215
  // extern pure def unsafeIndexOf(str: String, sub: String): Int =
223
216
  // js "${str}.indexOf(${sub})"
224
217
 
225
-
226
- // Characters
227
- // ----------
228
- //
229
- // JS: Int (Unicode codepoints)
230
- // Chez: ?
231
- // LLVM: i64 representing utf-8 (varying length 1-4 bytes)
232
-
233
- extern pure def toString(ch: Char): String =
234
- js "String.fromCodePoint(${ch})"
235
- chez "(string (integer->char ${ch}))"
236
- llvm """
237
- %z = call %Pos @c_bytearray_show_Char(%Int ${ch})
238
- ret %Pos %z
239
- """
240
-
241
- // Since we currently represent Char by integers in all backends, we could reuse comparison
242
- extern pure def toInt(ch: Char): Int =
243
- js "${ch}"
244
- chez "${ch}"
245
- llvm "ret %Int ${ch}"
246
- vm "string::toInt(Char)"
247
-
248
- extern pure def toChar(codepoint: Int): Char =
249
- js "${codepoint}"
250
- chez "${codepoint}"
251
- llvm "ret %Int ${codepoint}"
252
- vm "string::toChar(Int)"
253
-
254
-
255
- extern pure def infixLt(x: Char, y: Char): Bool =
256
- js "(${x} < ${y})"
257
- chez "(< ${x} ${y})"
258
- llvm """
259
- %z = icmp slt %Int ${x}, ${y}
260
- %fat_z = zext i1 %z to i64
261
- %adt_boolean = insertvalue %Pos zeroinitializer, i64 %fat_z, 0
262
- ret %Pos %adt_boolean
263
- """
264
- vm "string::infixLt(Char, Char)"
265
-
266
- extern pure def infixLte(x: Char, y: Char): Bool =
267
- js "(${x} <= ${y})"
268
- chez "(<= ${x} ${y})"
269
- llvm """
270
- %z = icmp sle %Int ${x}, ${y}
271
- %fat_z = zext i1 %z to i64
272
- %adt_boolean = insertvalue %Pos zeroinitializer, i64 %fat_z, 0
273
- ret %Pos %adt_boolean
274
- """
275
- vm "string::infixLte(Char, Char)"
276
-
277
- extern pure def infixGt(x: Char, y: Char): Bool =
278
- js "(${x} > ${y})"
279
- chez "(> ${x} ${y})"
280
- llvm """
281
- %z = icmp sgt %Int ${x}, ${y}
282
- %fat_z = zext i1 %z to i64
283
- %adt_boolean = insertvalue %Pos zeroinitializer, i64 %fat_z, 0
284
- ret %Pos %adt_boolean
285
- """
286
- vm "string::infixGt(Char, Char)"
287
-
288
- extern pure def infixGte(x: Char, y: Char): Bool =
289
- js "(${x} >= ${y})"
290
- chez "(>= ${x} ${y})"
291
- llvm """
292
- %z = icmp sge %Int ${x}, ${y}
293
- %fat_z = zext i1 %z to i64
294
- %adt_boolean = insertvalue %Pos zeroinitializer, i64 %fat_z, 0
295
- ret %Pos %adt_boolean
296
- """
297
- vm "string::infixGte(Char, Char)"
298
-
299
-
300
- /**
301
- * Determines the number of bytes needed by a codepoint
302
- *
303
- * Also see: https://en.wikipedia.org/wiki/UTF-8
304
- */
305
- def utf8ByteCount(codepoint: Char): Int = codepoint match {
306
- case c and c >= \u0000 and c <= \u007F => 1
307
- case c and c >= \u0080 and c <= \u07FF => 2
308
- case c and c >= \u0800 and c <= \uFFFF => 3
309
- case c and c >= \u10000 and c <= \u10FFFF => 4
310
- case c => panic("Not a valid code point")
311
- }
312
-
313
- def utf16UnitCount(codepoint: Char): Int = codepoint match {
314
- case c and c >= \u0000 and c <= \uFFFF => 1
315
- case c and c >= \u10000 and c <= \u10FFFF => 4
316
- case c => panic("Not a valid code point")
317
- }
318
-
319
- extern pure def charWidth(c: Char): Int =
320
- // JavaScript strings are UTF-16 where every unicode character after 0xffff takes two units
321
- js "(${c} > 0xffff) ? 2 : 1"
322
-
323
218
  def charAt(str: String, index: Int): Char / Exception[OutOfBounds] =
324
219
  if (index < 0 || index >= length(str))
325
220
  do raise(OutOfBounds(), "Index out of bounds: " ++ show(index) ++ " in string: '" ++ str ++ "'")
@@ -58,4 +58,4 @@ $effekt.unit = { __unit: true }
58
58
 
59
59
  $effekt.emptyMatch = function() { throw "empty match" }
60
60
 
61
- $effekt.hole = function() { throw "not implemented, yet" }
61
+ $effekt.hole = function() { throw "not implemented, yet" }
@@ -72,7 +72,7 @@ struct Pos c_bytearray_from_nullterminated_string(const char *data) {
72
72
  char* c_bytearray_into_nullterminated_string(const struct Pos arr) {
73
73
  uint64_t size = arr.tag;
74
74
 
75
- char* result = (char*)malloc(size + 1);
75
+ char* result = malloc(size + 1);
76
76
 
77
77
  memcpy(result, c_bytearray_data(arr), size);
78
78
 
@@ -3,10 +3,8 @@
3
3
  declare i64 @c_get_argc()
4
4
  declare %Pos @c_get_arg(i64)
5
5
 
6
- declare void @c_io_println_Boolean(%Pos)
7
- declare void @c_io_println_Int(%Int)
8
- declare void @c_io_println_Double(%Double)
9
- declare void @c_io_println_String(%Pos)
6
+ declare void @c_io_println(%Pos)
7
+ declare %Pos @c_io_readln()
10
8
 
11
9
  declare void @hole()
12
10
  declare void @duplicated_prompt()
@@ -4,28 +4,35 @@
4
4
  #include <uv.h>
5
5
  #include <string.h> // to compare flag names
6
6
 
7
- // Println
8
- // -------
7
+ // Println and Readln
8
+ // ------------------
9
9
 
10
- void c_io_println_Int(const Int n) {
11
- printf("%" PRId64 "\n", n);
12
- }
13
-
14
- void c_io_println_Boolean(const struct Pos p) {
15
- printf("%s\n", p.tag ? "true" : "false");
16
- }
17
-
18
- void c_io_println_Double(const Double x) {
19
- printf("%g\n", x);
20
- }
21
-
22
- void c_io_println_String(String text) {
23
- for (uint64_t j = 0; j < text.tag; ++j)
10
+ void c_io_println(String text) {
11
+ for (uint64_t j = 0; j < text.tag; ++j) {
24
12
  putchar(c_bytearray_data(text)[j]);
13
+ };
25
14
  erasePositive(text);
26
15
  putchar('\n');
27
16
  }
28
17
 
18
+ String c_io_readln() {
19
+ uint64_t capacity = 64;
20
+ uint64_t length = 0;
21
+ char* buffer = malloc(capacity * sizeof(char));
22
+
23
+ for (int c = getchar(); c != '\n' && c != EOF; c = getchar()) {
24
+ if (length >= capacity) {
25
+ capacity *= 2;
26
+ buffer = realloc(buffer, capacity * sizeof(char));
27
+ }
28
+ buffer[length] = (char)c;
29
+ length++;
30
+ }
31
+
32
+ String result = c_bytearray_construct(length, (const uint8_t*)buffer);
33
+ free(buffer);
34
+ return result;
35
+ }
29
36
 
30
37
  // Lib UV Bindings
31
38
  // ---------------
@@ -1,5 +1,5 @@
1
1
  <!doctype html><html lang=en><head><meta charset=utf-8><meta http-equiv=X-UA-Compatible content="IE=edge"><meta name=viewport content="width=device-width,initial-scale=1"><title>Eclipse Public License 2.0 (EPL) | The Eclipse Foundation</title>
2
- <meta property="og:title" content="Eclipse Public License 2.0 (EPL) | The Eclipse Foundation"><meta name=twitter:title content="Eclipse Public License 2.0 (EPL) | The Eclipse Foundation"><meta name=description content="The Eclipse Foundation provides our global community of individuals and organisations with a mature, scalable, and business-friendly environment for open source …"><meta property="og:description" content="The Eclipse Foundation provides our global community of individuals and organisations with a mature, scalable, and business-friendly environment for open source …"><meta name=twitter:description content="The Eclipse Foundation provides our global community of individuals and organisations with a mature, scalable, and business-friendly environment for open source …"><meta name=author content="Wayne Beaton"><link href=/favicon.ico rel=icon type=image/x-icon><meta property="og:image" content="https://www.eclipse.org/images/logos/eclipse-foundation-400x400.png"><meta name=twitter:image content="https://www.eclipse.org/images/logos/eclipse-foundation-400x400.png"><meta name=twitter:card content="summary"><meta name=twitter:site content="@eclipsefdn"><meta name=twitter:creator content="@eclipsefdn"><meta property="og:url" content="https://www.eclipse.org/legal/epl-2.0/"><meta property="og:type" content="website"><meta property="og:site_name" content="Eclipse Foundation"><meta name=keywords content="epl,epl-2.0,legal,foundation,eclipse,eclipse public license,license,licenses"><meta name=generator content="Hugo 0.144.2"><link rel=canonical href=https://www.eclipse.org/legal/epl-2.0/><link rel=alternate href=/legal/epl-2.0/index.xml type=application/rss+xml title><link rel=stylesheet href="/public/css/styles.css?v=1746987328"><script>(function(e,t,n,s,o){e[s]=e[s]||[],e[s].push({"gtm.start":(new Date).getTime(),event:"gtm.js"});var a=t.getElementsByTagName(n)[0],i=t.createElement(n),r=s!="dataLayer"?"&l="+s:"";i.async=!0,i.src="https://www.googletagmanager.com/gtm.js?id="+o+r,a.parentNode.insertBefore(i,a)})(window,document,"script","dataLayer","GTM-5WLCZXC")</script><script>var tableClasses="table"</script><link href="https://fonts.googleapis.com/css2?family=Roboto:ital,wght@0,100;0,300;0,400;0,500;0,700;0,900;1,100;1,300;1,400;1,500;1,700;1,900&display=swap" rel=stylesheet type=text/css></head><body><noscript><iframe src="https://www.googletagmanager.com/ns.html?id=GTM-5WLCZXC" height=0 width=0 style=display:none;visibility:hidden></iframe></noscript><a class=sr-only href=#content>Skip to main content</a><header class="header-wrapper header-wrapper" id=header><div class=header-toolbar><div class=container><div class=header-toolbar-row><div class="toolbar-btn toolbar-search-btn dropdown"><button class=dropdown-toggle id=toolbar-search type=button data-toggle=dropdown tabindex=0 aria-label=Search>
2
+ <meta property="og:title" content="Eclipse Public License 2.0 (EPL) | The Eclipse Foundation"><meta name=twitter:title content="Eclipse Public License 2.0 (EPL) | The Eclipse Foundation"><meta name=description content="The Eclipse Foundation provides our global community of individuals and organisations with a mature, scalable, and business-friendly environment for open source …"><meta property="og:description" content="The Eclipse Foundation provides our global community of individuals and organisations with a mature, scalable, and business-friendly environment for open source …"><meta name=twitter:description content="The Eclipse Foundation provides our global community of individuals and organisations with a mature, scalable, and business-friendly environment for open source …"><meta name=author content="Wayne Beaton"><link href=/favicon.ico rel=icon type=image/x-icon><meta property="og:image" content="https://www.eclipse.org/images/logos/eclipse-foundation-400x400.png"><meta name=twitter:image content="https://www.eclipse.org/images/logos/eclipse-foundation-400x400.png"><meta name=twitter:card content="summary"><meta name=twitter:site content="@eclipsefdn"><meta name=twitter:creator content="@eclipsefdn"><meta property="og:url" content="https://www.eclipse.org/legal/epl-2.0/"><meta property="og:type" content="website"><meta property="og:site_name" content="Eclipse Foundation"><meta name=keywords content="epl,epl-2.0,legal,foundation,eclipse,eclipse public license,license,licenses"><meta name=generator content="Hugo 0.144.2"><link rel=canonical href=https://www.eclipse.org/legal/epl-2.0/><link rel=alternate href=/legal/epl-2.0/index.xml type=application/rss+xml title><link rel=stylesheet href="/public/css/styles.css?v=1747592132"><script>(function(e,t,n,s,o){e[s]=e[s]||[],e[s].push({"gtm.start":(new Date).getTime(),event:"gtm.js"});var a=t.getElementsByTagName(n)[0],i=t.createElement(n),r=s!="dataLayer"?"&l="+s:"";i.async=!0,i.src="https://www.googletagmanager.com/gtm.js?id="+o+r,a.parentNode.insertBefore(i,a)})(window,document,"script","dataLayer","GTM-5WLCZXC")</script><script>var tableClasses="table"</script><link href="https://fonts.googleapis.com/css2?family=Roboto:ital,wght@0,100;0,300;0,400;0,500;0,700;0,900;1,100;1,300;1,400;1,500;1,700;1,900&display=swap" rel=stylesheet type=text/css></head><body><noscript><iframe src="https://www.googletagmanager.com/ns.html?id=GTM-5WLCZXC" height=0 width=0 style=display:none;visibility:hidden></iframe></noscript><a class=sr-only href=#content>Skip to main content</a><header class="header-wrapper header-wrapper" id=header><div class=header-toolbar><div class=container><div class=header-toolbar-row><div class="toolbar-btn toolbar-search-btn dropdown"><button class=dropdown-toggle id=toolbar-search type=button data-toggle=dropdown tabindex=0 aria-label=Search>
3
3
  <i class="fa fa-search fa-lg" aria-role=none></i></button><div class="toolbar-search-bar-wrapper dropdown-menu dropdown-menu-right" aria-labelledby=toolbar-search><form action=https://www.eclipse.org/home/search method=get><div class=search-bar><input class=search-bar-input name=q placeholder=Search>
4
4
  <button>
5
5
  <i class="fa fa-search" type=submit></i></button></div></form></div></div><div class="toolbar-btn toolbar-user-menu-btn dropdown"><button class=dropdown-toggle id=toolbar-user-menu type=button data-toggle=dropdown tabindex=0 aria-label="User Menu">
@@ -60,4 +60,4 @@ open source software collaboration and innovation.</p></div><div class=mega-menu
60
60
  <i class="fa-brands fa-linkedin-in fa-stack-1x fa-inverse" aria-hidden=true></i>
61
61
  <span class=sr-only>Linkedin account</span></span></a></li></ul></div></div><div class="footer-sections row equal-height-md font-bold"><div class=col-md-15><div class=row><section id=footer-eclipse-foundation class="footer-section col-sm-8"><div class=menu-heading>Eclipse Foundation</div><ul class=nav><li><a href=https://www.eclipse.org/org/>About</a></li><li><a href=https://projects.eclipse.org/>Projects</a></li><li><a href=https://www.eclipse.org/collaborations/>Collaborations</a></li><li><a href=https://www.eclipse.org/membership/>Membership</a></li><li><a href=https://www.eclipse.org/sponsor/>Sponsor</a></li></ul></section><section id=footer-legal class="footer-section col-sm-8"><div class=menu-heading>Legal</div><ul class=nav><li><a href=https://www.eclipse.org/legal/privacy/>Privacy Policy</a></li><li><a href=https://www.eclipse.org/legal/terms-of-use/>Terms of Use</a></li><li><a href=https://www.eclipse.org/legal/compliance/>Compliance</a></li><li><a href=https://www.eclipse.org/org/documents/Community_Code_of_Conduct.php>Code of Conduct</a></li><li><a href=https://www.eclipse.org/legal/>Legal Resources</a></li><li><a class=toolbar-manage-cookies href=# onclick=event.preventDefault()>Manage Cookies</a></li></ul></section><section id=footer-more class="footer-section col-sm-8"><div class=menu-heading>More</div><ul class=nav><li><a href=https://www.eclipse.org/security/>Report a Vulnerability</a></li><li><a href=https://status.eclipse.org/>Service Status</a></li><li><a href=https://www.eclipse.org/org/foundation/contact.php>Contact Us</a></li><li><a href=https://www.eclipse.org/projects/support/>Support</a></li></ul></section></div></div><div id=footer-end class="footer-section col-md-8 col-md-offset-1 col-sm-24"><div class=footer-end-newsletter><form id=mc-embedded-subscribe-form action="https://eclipse.us6.list-manage.com/subscribe/post?u=eaf9e1f06f194eadc66788a85&amp;id=e7538485cd&amp;f_id=00f9c2e1f0" method=post novalidate target=_blank><label class=footer-end-newsletter-label for=email>Subscribe to our Newsletter</label><div class=footer-end-newsletter-input-wrapper><input class=footer-end-newsletter-input type=email id=email name=EMAIL autocomplete=email placeholder="Enter your email address">
62
62
  <button class="footer-end-newsletter-submit btn btn-link" id=mc-embedded-subscribe type=submit name=subscribe>
63
- <i class="fa fa-solid fa-envelope fa-lg" aria-hidden=true></i></button></div></form></div></div></div><div class="col-sm-24 margin-top-20"><div class=row><div id=copyright class=col-md-16><p id=copyright-text>Copyright &copy; Eclipse Foundation AISBL. All Rights Reserved.</p></div></div></div><a href=# class=scrollup>Back to the top</a></div></footer><div id=footer-js><script src="/public/js/main.js?v1.0?v=1746987328"></script><script>(function(){})(jQuery,document)</script></div></body></html>
63
+ <i class="fa fa-solid fa-envelope fa-lg" aria-hidden=true></i></button></div></form></div></div></div><div class="col-sm-24 margin-top-20"><div class=row><div id=copyright class=col-md-16><p id=copyright-text>Copyright &copy; Eclipse Foundation AISBL. All Rights Reserved.</p></div></div></div><a href=# class=scrollup>Back to the top</a></div></footer><div id=footer-js><script src="/public/js/main.js?v1.0?v=1747592132"></script><script>(function(){})(jQuery,document)</script></div></body></html>