From 14c03b7c26ff06835fca631eed6a3e308946321a Mon Sep 17 00:00:00 2001 From: _ov4 Date: Sun, 18 Oct 2026 08:16:01 +0000 Subject: feat(lib): add experimental _EXP_STRING backed by std::string Co-Authored-By: Claude Opus 5.5 --- pseudocode-lib/README.md | 20 +++++++++++ pseudocode-lib/_exp_string.cc | 75 ++++++++++++++++++++++++++++++++++++++++++ pseudocode-lib/_exp_string.cps | 67 +++++++++++++++++++++++++++++++++++++ 3 files changed, 162 insertions(+) create mode 100644 pseudocode-lib/README.md create mode 100644 pseudocode-lib/_exp_string.cc create mode 100644 pseudocode-lib/_exp_string.cps (limited to 'pseudocode-lib') diff --git a/pseudocode-lib/README.md b/pseudocode-lib/README.md new file mode 100644 index 0000000..92a4bbb --- /dev/null +++ b/pseudocode-lib/README.md @@ -0,0 +1,20 @@ +# Experimental string library + +Build with `cmake --build build`. Include the built library in your program and pass it as a compiler input: + +```text +INCLUDE "build/pseudocode-lib/_exp_string.cps" +DECLARE Greeting : _EXP_STRING((C_STRING)"hello") +CALL _EXP_STRING_OUTPUT(Greeting & _EXP_STRING_NUM_TO_STR(42)) +``` + +```sh +build/cpsc prog.cps build/pseudocode-lib/_exp_string.cps +build/cpsc prog.cps build/pseudocode-lib/_exp_string.cps -r +``` + +INCLUDE paths are relative to the program. Adjust them when the program is elsewhere. + +`_EXP_STRING` owns an unbounded `std::string` through one handle. Constructors accept no arguments, another `_EXP_STRING`, `C_STRING`, or `(INTEGER count, C_CHAR character)`. Cast literals to C types explicitly; built-in STRING is not a constructor argument. Copy construction and assignment own independent strings. `_HANDLE` is public only for library implementation access; do not modify or free it. + +The first version provides `&`, `_EXP_STRING_NUM_TO_STR` (INTEGER/REAL), `_EXP_STRING_STR_TO_NUM` (REAL), `_EXP_STRING_LENGTH` (byte length) and `_EXP_STRING_OUTPUT` (text plus newline). Numeric formatting uses `std::to_string`; parsing uses `std::stod` and requires full consumption. C++ standard exceptions print the user call line and are catchable with TRY/CATCH. The built-in STRING remains available with its existing lifetime and NUL limitations. Owning class values inside whole-record or BYVAL-array copies still have bitwise-copy limitations. diff --git a/pseudocode-lib/_exp_string.cc b/pseudocode-lib/_exp_string.cc new file mode 100644 index 0000000..248b718 --- /dev/null +++ b/pseudocode-lib/_exp_string.cc @@ -0,0 +1,75 @@ +#include +#include +#include +#include +#include + +extern "C" int __cps_user_line; + +namespace { +template +decltype(auto) checked(Operation &&Op) { + try { + return std::forward(Op)(); + } catch (const std::exception &Error) { + std::fprintf(stderr, "[Fatal] line %d: %s\n", __cps_user_line, Error.what()); + throw 0; + } +} +std::string &text(void *Handle) { return *static_cast(Handle); } +} + +extern "C" { +void *_exp_string_impl_new() { + return checked([] { return new std::string; }); +} +void *_exp_string_impl_copy(void *Other) { + return checked([&] { return new std::string(text(Other)); }); +} +void *_exp_string_impl_from_cstr(const char *Text) { + return checked([&] { return new std::string(Text); }); +} +void *_exp_string_impl_from_count_char(long long Count, char Ch) { + return checked([&] { + if (Count < 0) throw std::invalid_argument("negative string length"); + return new std::string(static_cast(Count), Ch); + }); +} +void _exp_string_impl_delete(void *Handle) { + checked([&] { delete static_cast(Handle); }); +} +void _exp_string_impl_assign(void *Target, void *Other) { + checked([&] { text(Target) = text(Other); }); +} +void _exp_string_impl_assign_cstr(void *Target, const char *Text) { + checked([&] { text(Target) = Text; }); +} +void _exp_string_impl_concat(void *Target, void *A, void *B) { + checked([&] { text(Target) = text(A) + text(B); }); +} +void _exp_string_impl_from_int(void *Target, long long Number) { + checked([&] { text(Target) = std::to_string(Number); }); +} +void _exp_string_impl_from_real(void *Target, double Number) { + checked([&] { text(Target) = std::to_string(Number); }); +} +double _exp_string_impl_to_real(void *Handle) { + return checked([&] { + std::size_t Consumed = 0; + double Number = std::stod(text(Handle), &Consumed); + if (Consumed != text(Handle).size()) + throw std::invalid_argument("unconsumed characters in string conversion"); + return Number; + }); +} +long long _exp_string_impl_length(void *Handle) { + return checked([&] { return static_cast(text(Handle).size()); }); +} +void _exp_string_impl_output(void *Handle) { + checked([&] { + const auto &Text = text(Handle); + std::fwrite(Text.data(), 1, Text.size(), stdout); + std::fputc('\n', stdout); + }); +} +} diff --git a/pseudocode-lib/_exp_string.cps b/pseudocode-lib/_exp_string.cps new file mode 100644 index 0000000..369e20f --- /dev/null +++ b/pseudocode-lib/_exp_string.cps @@ -0,0 +1,67 @@ +#LIB +INCLUDE_OBJ "_exp_string.o" +TYPE _EXP_STRING_HANDLE = C_TYPE("void*") +TYPE _EXP_STRING_C_INTEGER = C_TYPE("long long") +EXTERN "C" EXTERN_FUNCTION _exp_string_impl_new() RETURNS _EXP_STRING_HANDLE +EXTERN "C" EXTERN_FUNCTION _exp_string_impl_copy(_EXP_STRING_HANDLE) RETURNS _EXP_STRING_HANDLE +EXTERN "C" EXTERN_FUNCTION _exp_string_impl_from_cstr(C_STRING) RETURNS _EXP_STRING_HANDLE +EXTERN "C" EXTERN_FUNCTION _exp_string_impl_from_count_char(_EXP_STRING_C_INTEGER, C_CHAR) RETURNS _EXP_STRING_HANDLE +EXTERN "C" EXTERN_PROCEDURE _exp_string_impl_delete(_EXP_STRING_HANDLE) +EXTERN "C" EXTERN_PROCEDURE _exp_string_impl_assign(_EXP_STRING_HANDLE, _EXP_STRING_HANDLE) +EXTERN "C" EXTERN_PROCEDURE _exp_string_impl_assign_cstr(_EXP_STRING_HANDLE, C_STRING) +EXTERN "C" EXTERN_PROCEDURE _exp_string_impl_concat(_EXP_STRING_HANDLE, _EXP_STRING_HANDLE, _EXP_STRING_HANDLE) +EXTERN "C" EXTERN_PROCEDURE _exp_string_impl_from_int(_EXP_STRING_HANDLE, _EXP_STRING_C_INTEGER) +EXTERN "C" EXTERN_PROCEDURE _exp_string_impl_from_real(_EXP_STRING_HANDLE, C_DOUBLE) +EXTERN "C" EXTERN_FUNCTION _exp_string_impl_to_real(_EXP_STRING_HANDLE) RETURNS C_DOUBLE +EXTERN "C" EXTERN_FUNCTION _exp_string_impl_length(_EXP_STRING_HANDLE) RETURNS _EXP_STRING_C_INTEGER +EXTERN "C" EXTERN_PROCEDURE _exp_string_impl_output(_EXP_STRING_HANDLE) + +CLASS _EXP_STRING + PUBLIC _HANDLE : _EXP_STRING_HANDLE + OVERLOAD PROCEDURE NEW() + _HANDLE <- _exp_string_impl_new() + ENDPROCEDURE + OVERLOAD PROCEDURE NEW(BYREF Other : _EXP_STRING) + _HANDLE <- _exp_string_impl_copy(Other._HANDLE) + ENDPROCEDURE + OVERLOAD PROCEDURE NEW(Text : C_STRING) + _HANDLE <- _exp_string_impl_from_cstr(Text) + ENDPROCEDURE + OVERLOAD PROCEDURE NEW(Count : INTEGER, Ch : C_CHAR) + _HANDLE <- _exp_string_impl_from_count_char((_EXP_STRING_C_INTEGER)Count, Ch) + ENDPROCEDURE + PROCEDURE DELETE() + CALL _exp_string_impl_delete(_HANDLE) + ENDPROCEDURE + OVERLOAD PROCEDURE OPERATOR<-(BYREF Other : _EXP_STRING) + CALL _exp_string_impl_assign(_HANDLE, Other._HANDLE) + ENDPROCEDURE + OVERLOAD PROCEDURE OPERATOR<-(Text : C_STRING) + CALL _exp_string_impl_assign_cstr(_HANDLE, Text) + ENDPROCEDURE +ENDCLASS + +OVERLOAD FUNCTION OPERATOR&(A : _EXP_STRING, B : _EXP_STRING) RETURNS _EXP_STRING + DECLARE Result : _EXP_STRING + CALL _exp_string_impl_concat(Result._HANDLE, A._HANDLE, B._HANDLE) + RETURN Result +ENDFUNCTION +OVERLOAD FUNCTION _EXP_STRING_NUM_TO_STR(Number : INTEGER) RETURNS _EXP_STRING + DECLARE Result : _EXP_STRING + CALL _exp_string_impl_from_int(Result._HANDLE, (_EXP_STRING_C_INTEGER)Number) + RETURN Result +ENDFUNCTION +OVERLOAD FUNCTION _EXP_STRING_NUM_TO_STR(Number : REAL) RETURNS _EXP_STRING + DECLARE Result : _EXP_STRING + CALL _exp_string_impl_from_real(Result._HANDLE, (C_DOUBLE)Number) + RETURN Result +ENDFUNCTION +FUNCTION _EXP_STRING_STR_TO_NUM(S : _EXP_STRING) RETURNS REAL + RETURN (REAL)_exp_string_impl_to_real(S._HANDLE) +ENDFUNCTION +FUNCTION _EXP_STRING_LENGTH(S : _EXP_STRING) RETURNS INTEGER + RETURN (INTEGER)_exp_string_impl_length(S._HANDLE) +ENDFUNCTION +PROCEDURE _EXP_STRING_OUTPUT(S : _EXP_STRING) + CALL _exp_string_impl_output(S._HANDLE) +ENDPROCEDURE -- cgit v1.3.1-4-g156e