diff --git a/libc/config/linux/aarch64/entrypoints.txt b/libc/config/linux/aarch64/entrypoints.txt --- a/libc/config/linux/aarch64/entrypoints.txt +++ b/libc/config/linux/aarch64/entrypoints.txt @@ -1,4 +1,7 @@ set(TARGET_LIBC_ENTRYPOINTS + # ctype.h entrypoints + libc.src.ctype.isalpha + # errno.h entrypoints libc.src.errno.__errno_location diff --git a/libc/config/linux/x86_64/entrypoints.txt b/libc/config/linux/x86_64/entrypoints.txt --- a/libc/config/linux/x86_64/entrypoints.txt +++ b/libc/config/linux/x86_64/entrypoints.txt @@ -1,6 +1,9 @@ set(TARGET_LIBC_ENTRYPOINTS # assert.h entrypoints libc.src.assert.__assert_fail + + # ctype.h entrypoints + libc.src.ctype.isalpha # errno.h entrypoints libc.src.errno.__errno_location diff --git a/libc/include/ctype.h.def b/libc/include/ctype.h.def new file mode 100644 --- /dev/null +++ b/libc/include/ctype.h.def @@ -0,0 +1,16 @@ +//===-- C standard library header ctype.h --------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_LIBC_CTYPE_H +#define LLVM_LIBC_CTYPE_H + +#include <__llvm-libc-common.h> + +%%public_api() + +#endif // LLVM_LIBC_CTYPE_H diff --git a/libc/spec/stdc.td b/libc/spec/stdc.td --- a/libc/spec/stdc.td +++ b/libc/spec/stdc.td @@ -39,7 +39,21 @@ [], // Enumerations [] >; - + + HeaderSpec CType = HeaderSpec< + "ctype.h", + [], // Macros + [], // Types + [], // Enumerations + [ + FunctionSpec< + "isalpha", + RetValSpec, + [ArgSpec] + >, + ] + >; + HeaderSpec String = HeaderSpec< "string.h", [ @@ -386,6 +400,7 @@ let Headers = [ Assert, + CType, Errno, Math, String, diff --git a/libc/src/CMakeLists.txt b/libc/src/CMakeLists.txt --- a/libc/src/CMakeLists.txt +++ b/libc/src/CMakeLists.txt @@ -1,4 +1,5 @@ add_subdirectory(assert) +add_subdirectory(ctype) add_subdirectory(errno) add_subdirectory(math) add_subdirectory(signal) diff --git a/libc/src/ctype/CMakeLists.txt b/libc/src/ctype/CMakeLists.txt new file mode 100644 --- /dev/null +++ b/libc/src/ctype/CMakeLists.txt @@ -0,0 +1,7 @@ +add_entrypoint_object( + isalpha + SRCS + isalpha.cpp + HDRS + isalpha.h +) diff --git a/libc/src/ctype/isalpha.h b/libc/src/ctype/isalpha.h new file mode 100644 --- /dev/null +++ b/libc/src/ctype/isalpha.h @@ -0,0 +1,20 @@ +//===-- Implementation header for isalpha -------------------------*-C++-*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_LIBC_SRC_CTYPE_ISALPHA_H +#define LLVM_LIBC_SRC_CTYPE_ISALPHA_H + +namespace __llvm_libc { + +// TODO: Currently restricted to default locale. +// These should be extended using locale information. +int isalpha(int c); + +} // namespace __llvm_libc + +#endif // LLVM_LIBC_SRC_CTYPE_ISALPHA_H diff --git a/libc/src/ctype/isalpha.cpp b/libc/src/ctype/isalpha.cpp new file mode 100644 --- /dev/null +++ b/libc/src/ctype/isalpha.cpp @@ -0,0 +1,20 @@ +//===-- Implementation of isalpha------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "src/ctype/isalpha.h" + +#include "src/__support/common.h" + +namespace __llvm_libc { + +int LLVM_LIBC_ENTRYPOINT(isalpha)(int c) { + const unsigned ch = c; + return (ch | 32) - 'a' < 26; +} + +} // namespace __llvm_libc diff --git a/libc/test/src/CMakeLists.txt b/libc/test/src/CMakeLists.txt --- a/libc/test/src/CMakeLists.txt +++ b/libc/test/src/CMakeLists.txt @@ -1,4 +1,5 @@ add_subdirectory(assert) +add_subdirectory(ctype) add_subdirectory(errno) add_subdirectory(math) add_subdirectory(signal) diff --git a/libc/test/src/ctype/CMakeLists.txt b/libc/test/src/ctype/CMakeLists.txt new file mode 100644 --- /dev/null +++ b/libc/test/src/ctype/CMakeLists.txt @@ -0,0 +1,11 @@ +add_libc_testsuite(libc_ctype_unittests) + +add_libc_unittest( + isalpha + SUITE + libc_ctype_unittests + SRCS + isalpha_test.cpp + DEPENDS + libc.src.ctype.isalpha +) diff --git a/libc/test/src/ctype/isalpha_test.cpp b/libc/test/src/ctype/isalpha_test.cpp new file mode 100644 --- /dev/null +++ b/libc/test/src/ctype/isalpha_test.cpp @@ -0,0 +1,57 @@ +//===-- Unittests for isalpha----------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "src/ctype/isalpha.h" +#include "utils/UnitTest/Test.h" + +// Helper function that makes a call to isalpha a bit cleaner +// for use with testing utilities, since it explicitly requires +// a boolean value for EXPECT_TRUE and EXPECT_FALSE. +bool call_isalpha(int c) { return __llvm_libc::isalpha(c); } + +TEST(IsAlpha, AllLowercaseLettersOfTheAlphabetShouldReturnTrue) { + for (int ch = 'a'; ch <= 'z'; ++ch) + EXPECT_TRUE(call_isalpha(ch)); +} + +TEST(IsAlpha, AllUppercaseLettersOfTheAlphabetShouldReturnTrue) { + for (int ch = 'A'; ch <= 'Z'; ++ch) + EXPECT_TRUE(call_isalpha(ch)); +} + +TEST(IsAlpha, PostFixOperatorOnBoundaryCharacterShouldStillWork) { + int character = 'Z'; + // This should return true, even though it has side effects. + // This may fail in the case of conditional logical operators + // being used in the 'isalpha' implementation. + EXPECT_TRUE(call_isalpha(character++)); + // Verify the next character is indeed not true. + ASSERT_FALSE(call_isalpha(character)); +} + +TEST(IsAlpha, BoundaryCharactersOfAlphabetReturnFalse) { + // Verify there is no off-by-one error. + EXPECT_FALSE(call_isalpha('a' - 1)); + EXPECT_FALSE(call_isalpha('z' + 1)); + EXPECT_FALSE(call_isalpha('A' - 1)); + EXPECT_FALSE(call_isalpha('Z' + 1)); +} + +TEST(IsAlpha, NumbersShouldReturnFalse) { + for (int ch = '0'; ch <= '9'; ++ch) + EXPECT_FALSE(call_isalpha(ch)); +} + +TEST(IsAlpha, ZeroShouldReturnFalse) { EXPECT_FALSE(call_isalpha(0)); } + +TEST(IsAlpha, NonAlphabetSymbolsShouldReturnFalse) { + EXPECT_FALSE(call_isalpha('?')); + EXPECT_FALSE(call_isalpha('~')); + EXPECT_FALSE(call_isalpha('+')); + EXPECT_FALSE(call_isalpha('\n')); +}