Skip to content

Commit

Permalink
Support data parsing operations (JetBrains#380)
Browse files Browse the repository at this point in the history
  • Loading branch information
olonho authored Mar 23, 2017
1 parent 8afa126 commit b5c60e3
Show file tree
Hide file tree
Showing 13 changed files with 534 additions and 5 deletions.
4 changes: 4 additions & 0 deletions FAQ.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
Q: How do I run my program?

A: Define top level function `fun main(args: Array<String>)`, please ensure it's not
in a package.
4 changes: 3 additions & 1 deletion RELEASE_NOTES.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,9 @@ the following platforms:

* Mac OS X 10.10 and later (x86-64)
* x86-64 Ubuntu Linux (14.04, 16.04 and later), other Linux flavours may work as well
* Apple iOS (arm64 and simulator on x86-64)
* Apple iOS (arm64 and simulator on x86-64), cross-compiled on MacOS X host
* Raspberry Pi, cross-compiled on Linux host


Adding support for other target platforms shalln't be too hard, if LLVM support
is available.
Expand Down
13 changes: 13 additions & 0 deletions backend.native/tests/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -1034,6 +1034,19 @@ task string0(type: RunKonanTest) {
source = "runtime/text/string0.kt"
}

task parse0(type: RunKonanTest) {
goldValue = "false\n" +
"true\n" +
"-1\n" +
"-86\n" +
"30\n" +
"4294967295\n" +
"bad format\n" +
"0.5\n" +
"2.39\n"
source = "runtime/text/parse0.kt"
}

task catch1(type: RunKonanTest) {
goldValue = "Before\nCaught Throwable\nDone\n"
source = "runtime/exceptions/catch1.kt"
Expand Down
15 changes: 15 additions & 0 deletions backend.native/tests/runtime/text/parse0.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
fun main(args: Array<String>) {
println("false".toBoolean())
println("true".toBoolean())
println("-1".toByte())
println("aa".toByte(16))
println("11110".toInt(2))
println("ffffffff".toLong(16))
try {
val x = "ffffffff".toLong(10)
} catch (ne: NumberFormatException) {
println("bad format")
}
println("0.5".toFloat())
println("2.39".toDouble())
}
4 changes: 3 additions & 1 deletion runtime/src/main/cpp/Exceptions.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,10 @@ void ThrowNullPointerException();
void ThrowArrayIndexOutOfBoundsException();
// Throws class cast exception.
void ThrowClassCastException();
// Throws arithmetic exception
// Throws arithmetic exception.
void ThrowArithmeticException();
// Throws number format exception.
void ThrowNumberFormatException();

#ifdef __cplusplus
} // extern "C"
Expand Down
92 changes: 92 additions & 0 deletions runtime/src/main/cpp/KString.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,58 @@ OBJ_GETTER(utf8ToUtf16, Raw<const char> rawString) {
RETURN_OBJ(result->obj());
}

template <typename T> T parseInt(KString value, KInt radix) {
Raw<const KChar> utf16(
CharArrayAddressOfElementAt(value, 0), value->count_);
std::string utf8;
utf8::utf16to8(utf16.begin(), utf16.end(), back_inserter(utf8));
char* end = nullptr;
long result = strtol(utf8.c_str(), &end, radix);
if (end != utf8.c_str() + utf8.size()) {
ThrowNumberFormatException();
}
return result;
}

KLong parseLong(KString value, KInt radix) {
Raw<const KChar> utf16(
CharArrayAddressOfElementAt(value, 0), value->count_);
std::string utf8;
utf8::utf16to8(utf16.begin(), utf16.end(), back_inserter(utf8));
char* end = nullptr;
KLong result = strtoll(utf8.c_str(), &end, radix);
if (end != utf8.c_str() + utf8.size()) {
ThrowNumberFormatException();
}
return result;
}

KFloat parseFloat(KString value) {
Raw<const KChar> utf16(
CharArrayAddressOfElementAt(value, 0), value->count_);
std::string utf8;
utf8::utf16to8(utf16.begin(), utf16.end(), back_inserter(utf8));
char* end = nullptr;
KFloat result = strtof(utf8.c_str(), &end);
if (end != utf8.c_str() + utf8.size()) {
ThrowNumberFormatException();
}
return result;
}

KDouble parseDouble(KString value) {
Raw<const KChar> utf16(
CharArrayAddressOfElementAt(value, 0), value->count_);
std::string utf8;
utf8::utf16to8(utf16.begin(), utf16.end(), back_inserter(utf8));
char* end = nullptr;
KDouble result = strtod(utf8.c_str(), &end);
if (end != utf8.c_str() + utf8.size()) {
ThrowNumberFormatException();
}
return result;
}

} // namespace

extern "C" {
Expand Down Expand Up @@ -301,6 +353,22 @@ KChar Kotlin_Char_toUpperCase(KChar ch) {
return towupper(ch);
}

KInt Kotlin_Char_digitOf(KChar ch, KInt radix) {
// TODO: make smarter and support full unicode.
const static KInt digits[] = {
0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
-1, -1, -1, -1, -1, -1, -1,
10, 11, 12, 13, 14, 15, 16, 17, 18, 19,
20, 21, 22, 23, 24, 25, 26, 27, 28, 29,
30, 31, 32, 33, 34, 35,
-1, -1, -1, -1, -1, -1,
10, 11, 12, 13, 14, 15, 16, 17, 18, 19,
20, 21, 22, 23, 24, 25, 26, 27, 28, 29,
30, 31, 32, 33, 34, 35 };
if (ch < 0x30 /* 0 */ || ch > 0x7a /* z */) return -1;
return digits[ch - 0x30];
}

KInt Kotlin_String_indexOfChar(KString thiz, KChar ch, KInt fromIndex) {
if (fromIndex < 0 || fromIndex > thiz->count_) {
return false;
Expand Down Expand Up @@ -430,4 +498,28 @@ OBJ_GETTER0(Kotlin_io_Console_readLine) {
RETURN_RESULT_OF(CreateStringFromCString, data);
}

KByte Kotlin_String_parseByte(KString value, KInt radix) {
return parseInt<KByte>(value, radix);
}

KShort Kotlin_String_parseShort(KString value, KInt radix) {
return parseInt<KShort>(value, radix);
}

KInt Kotlin_String_parseInt(KString value, KInt radix) {
return parseInt<KInt>(value, radix);
}

KLong Kotlin_String_parseLong(KString value, KInt radix) {
return parseLong(value, radix);
}

KFloat Kotlin_String_parseFloat(KString value) {
return parseFloat(value);
}

KDouble Kotlin_String_parseDouble(KString value) {
return parseDouble(value);
}

} // extern "C"
38 changes: 38 additions & 0 deletions runtime/src/main/cpp/ToString.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -43,12 +43,50 @@ OBJ_GETTER(Kotlin_Int_toString, KInt value) {
RETURN_RESULT_OF(CreateStringFromCString, cstring);
}

OBJ_GETTER(Kotlin_Int_toStringRadix, KInt value, KInt radix) {
// TODO: maye not fit for smaller radices.
char cstring[32];
switch (radix) {
case 8:
snprintf(cstring, sizeof(cstring), "%o", value);
break;
case 10:
snprintf(cstring, sizeof(cstring), "%d", value);
break;
case 16:
snprintf(cstring, sizeof(cstring), "%x", value);
break;
default:
RuntimeAssert(false, "Unsupported radix");
}
RETURN_RESULT_OF(CreateStringFromCString, cstring);
}

OBJ_GETTER(Kotlin_Long_toString, KLong value) {
char cstring[32];
snprintf(cstring, sizeof(cstring), "%lld", static_cast<long long>(value));
RETURN_RESULT_OF(CreateStringFromCString, cstring);
}

OBJ_GETTER(Kotlin_Long_toStringRadix, KLong value, KInt radix) {
// TODO: may not fit for smaller radices.
char cstring[64];
switch (radix) {
case 8:
snprintf(cstring, sizeof(cstring), "%llo", value);
break;
case 10:
snprintf(cstring, sizeof(cstring), "%lld", value);
break;
case 16:
snprintf(cstring, sizeof(cstring), "%llx", value);
break;
default:
RuntimeAssert(false, "Unsupported radix");
}
RETURN_RESULT_OF(CreateStringFromCString, cstring);
}

// TODO: use David Gay's dtoa() here instead. It's *very* big and ugly.
OBJ_GETTER(Kotlin_Float_toString, KFloat value) {
char cstring[32];
Expand Down
5 changes: 5 additions & 0 deletions runtime/src/main/kotlin/konan/internal/RuntimeUtils.kt
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,11 @@ internal fun ThrowArithmeticException() : Nothing {
throw ArithmeticException()
}

@ExportForCppRuntime
internal fun ThrowNumberFormatException() : Nothing {
throw NumberFormatException()
}

fun ThrowNoWhenBranchMatchedException(): Nothing {
throw NoWhenBranchMatchedException()
}
Expand Down
10 changes: 10 additions & 0 deletions runtime/src/main/kotlin/kotlin/Char.kt
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,16 @@ public final class Char : Comparable<Char> {
* The maximum value of a Unicode surrogate code unit.
*/
public const val MAX_SURROGATE: Char = MAX_LOW_SURROGATE

/**
* The minimum radix available for conversion to and from strings.
*/
public const val MIN_RADIX: Int = 2

/**
* The minimum radix available for conversion to and from strings.
*/
public const val MAX_RADIX: Int = 36
}

// Konan-specific.
Expand Down
8 changes: 8 additions & 0 deletions runtime/src/main/kotlin/kotlin/Exceptions.kt
Original file line number Diff line number Diff line change
Expand Up @@ -192,4 +192,12 @@ public open class OutOfMemoryError : Error {

constructor(s: String) : super(s) {
}
}

public open class NumberFormatException : IllegalArgumentException {

constructor() : super() {
}

constructor(s: String) : super(s) {}
}
16 changes: 15 additions & 1 deletion runtime/src/main/kotlin/kotlin/text/Char.kt
Original file line number Diff line number Diff line change
Expand Up @@ -78,4 +78,18 @@ external public fun Char.isHighSurrogate(): Boolean
* Returns `true` if this character is a Unicode low-surrogate code unit (also known as trailing-surrogate code unit).
*/
@SymbolName("Kotlin_Char_isLowSurrogate")
external public fun Char.isLowSurrogate(): Boolean
external public fun Char.isLowSurrogate(): Boolean

@SymbolName("Kotlin_Char_digitOf")
external internal fun digitOf(char: Char, radix: Int): Int

/**
* Checks whether the given [radix] is valid radix for string to number and number to string conversion.
*/
@PublishedApi
internal fun checkRadix(radix: Int): Int {
if(radix !in Char.MIN_RADIX..Char.MAX_RADIX) {
throw IllegalArgumentException("radix $radix was not in valid range ${Char.MIN_RADIX..Char.MAX_RADIX}")
}
return radix
}
Loading

0 comments on commit b5c60e3

Please sign in to comment.