Skip to content

Commit

Permalink
Add string_to_Int to predef (#1307)
Browse files Browse the repository at this point in the history
  • Loading branch information
johnynek authored Dec 11, 2024
1 parent c2aa43f commit 34a90df
Show file tree
Hide file tree
Showing 9 changed files with 117 additions and 0 deletions.
4 changes: 4 additions & 0 deletions c_runtime/bosatsu_ext_Bosatsu_l_Predef.c
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,10 @@ BValue ___bsts_g_Bosatsu_l_Predef_l_string__Order__fn(BValue a, BValue b) {
return alloc_enum0(result + 1);
}

BValue ___bsts_g_Bosatsu_l_Predef_l_string__to__Int(BValue a) {
return bsts_string_to_integer(a);
}

BValue ___bsts_g_Bosatsu_l_Predef_l_sub(BValue a, BValue b) {
return ___bsts_g_Bosatsu_l_Predef_l_add(a, bsts_integer_negate(b));
}
Expand Down
2 changes: 2 additions & 0 deletions c_runtime/bosatsu_ext_Bosatsu_l_Predef.h
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@ BValue ___bsts_g_Bosatsu_l_Predef_l_shift__right__Int(BValue a, BValue b);

BValue ___bsts_g_Bosatsu_l_Predef_l_string__Order__fn(BValue a, BValue b);

BValue ___bsts_g_Bosatsu_l_Predef_l_string__to__Int(BValue a);

BValue ___bsts_g_Bosatsu_l_Predef_l_sub(BValue a, BValue b);

BValue ___bsts_g_Bosatsu_l_Predef_l_times(BValue a, BValue b);
Expand Down
44 changes: 44 additions & 0 deletions c_runtime/bosatsu_runtime.c
Original file line number Diff line number Diff line change
Expand Up @@ -1105,6 +1105,50 @@ BValue bsts_integer_to_string(BValue v) {
}
}

// String -> Option[Integer]
BValue bsts_string_to_integer(BValue v) {
size_t slen = bsts_string_utf8_len(v);
char* bytes = bsts_string_utf8_bytes(v);
if (slen == 0) return alloc_enum0(0);

size_t pos = 0;
_Bool sign = 0;
if (bytes[pos] == '-') {
sign = 1;
pos++;
if (slen == 1) return alloc_enum0(0);
}
// at least 1 character

int64_t acc = 0;
BValue bacc = 0;
while(pos < slen) {
int32_t digit = (int32_t)(bytes[pos] - '0');
if ((digit < 0) || (9 < digit)) return alloc_enum0(0);
if (pos >= 10) {
if (pos == 10) {
// we could be overflowing an int32_t at this point
bacc = bsts_integer_from_int64(((int64_t)acc) * 10L);
}
else {
bacc = bsts_integer_times(bacc, bsts_integer_from_int(10));
}
bacc = bsts_integer_add(bacc, bsts_integer_from_int(digit));
}
else {
acc = acc * 10 + digit;
}
pos++;
}
if (slen < 11) {
// acc should hold the number
return alloc_enum1(1, bsts_integer_from_int64(sign ? -acc : acc));
}
else {
return alloc_enum1(1, sign ? bsts_integer_negate(bacc) : bacc);
}
}

// Function to convert sign-magnitude to two's complement representation
void sign_magnitude_to_twos_complement(_Bool sign, size_t len, uint32_t* words, uint32_t* result_words, size_t result_len) {
memcpy(result_words, words, len * sizeof(uint32_t));
Expand Down
2 changes: 2 additions & 0 deletions c_runtime/bosatsu_runtime.h
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,8 @@ int bsts_integer_cmp(BValue l, BValue r);
BValue bsts_integer_negate(BValue v);
// &Integer -> String
BValue bsts_integer_to_string(BValue v);
// String -> Option[Integer]
BValue bsts_string_to_integer(BValue v);
// (&Integer, &Integer) -> (Integer, Integer)
// div_mod(l, r) == (d, m) <=> l = r * d + m
BValue bsts_integer_div_mod(BValue l, BValue r);
Expand Down
2 changes: 2 additions & 0 deletions core/src/main/resources/bosatsu/predef.bosatsu
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ export (
get_key,
int_loop,
int_to_String,
string_to_Int,
items,
map_List,
mod_Int,
Expand Down Expand Up @@ -249,6 +250,7 @@ external def partition_String(arg: String, sep: String) -> Option[(String, Strin
external def rpartition_String(arg: String, sep: String) -> Option[(String, String)]

external def int_to_String(i: Int) -> String
external def string_to_Int(s: String) -> Option[Int]

external def trace(prefix: String, item: a) -> a

Expand Down
13 changes: 13 additions & 0 deletions core/src/main/scala/org/bykn/bosatsu/Predef.scala
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,11 @@ object Predef {
"int_to_String",
FfiCall.Fn1(PredefImpl.int_to_String(_))
)
.add(
packageName,
"string_to_Int",
FfiCall.Fn1(PredefImpl.string_to_Int(_))
)
.add(packageName, "trace", FfiCall.Fn2(PredefImpl.trace(_, _)))
.add(
packageName,
Expand Down Expand Up @@ -226,6 +231,14 @@ object PredefImpl {
final def int_to_String(intValue: Value): Value =
Value.Str(i(intValue).toString)

final def string_to_Int(strValue: Value): Value = {
val Value.Str(str) = strValue
try Value.VOption.some(VInt(new BigInteger(str)))
catch {
case _: NumberFormatException => Value.VOption.none
}
}

def trace(prefix: Value, v: Value): Value = {
val Value.Str(prestr) = prefix
println(s"$prestr: $v")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -969,6 +969,7 @@ object Code {
case Const.Plus => (that == Const.Plus) || (that == Const.Minus)
case Const.Minus => false
case Const.And => that == Const.And
case Const.Or => that == Const.Or
case Const.Times =>
// (a * b) * c == a * (b * c)
// (a * b) + c != a * (b + c)
Expand Down Expand Up @@ -1008,6 +1009,7 @@ object Code {
case object BitwiseShiftLeft extends IntOp("<<")
case object BitwiseShiftRight extends IntOp(">>")
case object And extends Operator("and")
case object Or extends Operator("or")
case object Eq extends Operator("==")
case object Neq extends Operator("!=")
case object Gt extends Operator(">")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -940,6 +940,28 @@ object PythonGen {
1
)
),
(
Identifier.unsafeBindable("string_to_Int"),
(
{ input =>
Env.onLast(input.head) { s =>
// int(s) if (s[0] == '-' and s[1:].isdigit()) or s.isdigit() else None
val isdigit = Code.Ident("isdigit")
val isValid = Code.Op(
(s.get(0) =:= Code.PyString("-")).evalAnd(
Code.SelectRange(s, Some(Code.Const.One), None).dot(isdigit)()
),
Code.Const.Or,
s.dot(isdigit)())

Code.Ternary(Code.MakeTuple(Code.Const.One :: Code.Ident("int")(s) :: Nil),
isValid,
Code.MakeTuple(Code.Const.Zero :: Nil))
}
},
1
)
),
(
Identifier.unsafeBindable("char_to_String"),
// we encode chars as strings so this is just identity
Expand Down
26 changes: 26 additions & 0 deletions test_workspace/PredefTests.bosatsu
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,11 @@ operator % = mod_Int

i = int_to_String

def oi(opt):
match opt:
case None: "None"
case Some(v): "Some(${i(v)})"

test_int = TestSuite("Int tests", [
Assertion((4 % -3) matches -2, "(4 % -3) == -2 got: ${i(4 % -3)}"),
Assertion((-8 % -2) matches 0, "(-8 % -2) == 0 got: ${i(-8 % -2)}"),
Expand Down Expand Up @@ -60,6 +65,27 @@ test_int = TestSuite("Int tests", [
Assertion(int_to_String(0) matches "0", "0 str"),
Assertion(int_to_String(123) matches "123", "123 str"),
Assertion(int_to_String(-123) matches "-123", "-123 str"),

Assertion(string_to_Int("123") matches Some(123), "123 string_to_Int"),
Assertion(string_to_Int("-123") matches Some(-123), "-123 string_to_Int"),
Assertion(string_to_Int("-123x") matches None, "-123x string_to_Int"),
Assertion(string_to_Int("-${int_to_String(123)}") matches Some(-123), "-123 string_to_Int"),

Assertion(string_to_Int("9223372036854775807") matches Some(9223372036854775807),
"Long.Max ${oi(string_to_Int("9223372036854775807"))} != ${oi(Some(9223372036854775807))}string_to_Int"),
Assertion(string_to_Int("9223372036854775808") matches Some(9223372036854775808),
"Long.Max + 1: ${oi(string_to_Int("9223372036854775808"))} string_to_Int"),

Assertion(string_to_Int("2147483647") matches Some(2147483647), "Int.Max string_to_Int"),
Assertion(string_to_Int("2147483648") matches Some(2147483648),
"Int.Max + 1 string_to_Int: ${oi(string_to_Int("2147483648"))} != ${oi(Some(2147483648))}"),

Assertion(string_to_Int("-9223372036854775808") matches Some(-9223372036854775808), "Long.Min string_to_Int"),
Assertion(string_to_Int("-9223372036854775809") matches Some(-9223372036854775809), "Long.Min - 1 string_to_Int"),

Assertion(string_to_Int("-2147483648") matches Some(-2147483648), "Int.Min string_to_Int"),
Assertion(string_to_Int("-2147483649") matches Some(-2147483649), "Int.Min - 1 string_to_Int"),
Assertion(string_to_Int("-2147483649z") matches None, "-2147483649z string_to_Int"),
])

test_string = TestSuite("String tests", [
Expand Down

0 comments on commit 34a90df

Please sign in to comment.