From 09498bb7aab9182ba172aae1045ca895d4f422d4 Mon Sep 17 00:00:00 2001 From: _ov4 Date: Sat, 17 Oct 2026 15:13:04 +0000 Subject: docs: document EXTERN and INCLUDE_OBJ Co-Authored-By: Claude Opus 5.5 --- AGENTS/ARCH.md | 3 +- AGENTS/PITFALLS.md | 8 ++++- AGENTS/SUBSYSTEMS.md | 13 +++++++ cmake/ManualAliases.cmake | 2 ++ doc/language/cpsc-language-overview.7.md | 6 ++-- doc/language/keyword/cpsc-keyword-c_type.7.md | 2 +- doc/language/keyword/cpsc-keyword-extern.7.md | 37 ++++++++++++++++++++ doc/language/keyword/cpsc-keyword-include_obj.7.md | 40 ++++++++++++++++++++++ 8 files changed, 106 insertions(+), 5 deletions(-) create mode 100644 doc/language/keyword/cpsc-keyword-extern.7.md create mode 100644 doc/language/keyword/cpsc-keyword-include_obj.7.md diff --git a/AGENTS/ARCH.md b/AGENTS/ARCH.md index cb0bc20..0479641 100644 --- a/AGENTS/ARCH.md +++ b/AGENTS/ARCH.md @@ -25,7 +25,8 @@ imports cover standard C integer/real types, _Bool, void and object pointers. CScalar/CPointer identities are canonical C types, not pseudocode alias names; native widths and signedness come from sizeof/type traits, while floating formats come from numeric_limits (long double size alone is insufficient). Programs use -the compiler's build ABI; cross-target emission and EXTERN are not implemented. +the compiler's build ABI; cross-target emission is not implemented. EXTERN "C" +declarations call C functions from INCLUDE_OBJ objects (see SUBSYSTEMS.md). CTypeGen handles typed numeric conversions, casts, native arithmetic and I/O. Crossing C/pseudocode domains requires a cast, including literals. C arithmetic uses C++ promotions, except that / always returns pseudocode REAL. diff --git a/AGENTS/PITFALLS.md b/AGENTS/PITFALLS.md index 8f35c3d..551a64c 100644 --- a/AGENTS/PITFALLS.md +++ b/AGENTS/PITFALLS.md @@ -145,7 +145,13 @@ test programs. require casts even for literals. C scalar arithmetic uses native promotions, but / always returns REAL. Cast aliases must precede their use. long double may have padding; sizeof does not identify its floating-point format. Native - C support is verified on Linux LLVM 23; cross-target and EXTERN are unsupported. + C support is verified on Linux LLVM 23; cross-target is unsupported. +- **EXTERN "C" extension.** Top-level, body-less; parameters/results are C types only, + no BYREF/OVERLOAD/structs. Variadic arguments must be C values (default promotions). + The driver checks every EXTERN symbol against INCLUDE_OBJ inputs before CodeGen; + `"__libc"` must be included explicitly even though the runtime always links libc. + Only `.o` user objects; INCLUDE_OBJ is global across inputs. `-S` does not link them. + Two EXTERNs differing only in case conflict (pseudocode names are case insensitive). ## Not implemented (9618 features) diff --git a/AGENTS/SUBSYSTEMS.md b/AGENTS/SUBSYSTEMS.md index dab3146..866d39e 100644 --- a/AGENTS/SUBSYSTEMS.md +++ b/AGENTS/SUBSYSTEMS.md @@ -43,6 +43,19 @@ compile error (no extern is fabricated). Functions inherit visible GLOBAL storag `StmtEmitter` callback, and must restore its own insert point (and the exchanged ArrayTable) afterwards. +**EXTERN "C"** (`ExternDeclAST`, `IncludeObjStmtAST`): the parser builds a body-less +`PrototypeAST` (IsExternal, IsVariadic, unnamed `argN` params). `FunctionGen::emitExternPrototype` +validates C types, records a `FuncSig` with `IsExternal`, the declared `FunctionType` and +sext/zext attributes for sub-32-bit integers, and `getOrInsertFunction`s the exact symbol. +Calls go through `FunctionGen::emitCall(const FuncSig &, ...)` with that type, so a +runtime declaration of the same libc symbol with another type is harmless. +`CodeGen::emitVariadicArgument` applies C default promotions. The driver (`main.cc`) +resolves INCLUDE_OBJ paths, reads defined global symbols with `llvm::object` +(`collectObjectSymbols`; `"__libc"` uses `collectLibcSymbols` over the embedded runtime's +libc/libm inputs, copied to aligned buffers), rejects missing symbols, and passes objects +to `compileNative`, `runEmbedded` (`addObjectFile`) or lli (`-extra-object=`). +Linux is verified; the Windows/macOS libc file-name rules and the lli path are not. + **User types** (`TypeSystem` + `Designator.cc` + `TypeCheckGen.cc`): `Designator.cc` owns the type pre-pass and lvalue access; `TypeCheckGen.cc` owns type admission and user-kind binary operators. TYPE declarations are top-level only diff --git a/cmake/ManualAliases.cmake b/cmake/ManualAliases.cmake index 452f8eb..858e6c9 100644 --- a/cmake/ManualAliases.cmake +++ b/cmake/ManualAliases.cmake @@ -28,6 +28,8 @@ set(CPSC_MANUAL_ALIASES "cpsc-keyword-char:cpsc-keyword-declare" "cpsc-keyword-string:cpsc-keyword-declare" "cpsc-keyword-endtype:cpsc-keyword-type" + "cpsc-keyword-extern_function:cpsc-keyword-extern" + "cpsc-keyword-extern_procedure:cpsc-keyword-extern" "cpsc-keyword-public:cpsc-keyword-class" "cpsc-keyword-private:cpsc-keyword-class" "cpsc-keyword-endclass:cpsc-keyword-class" diff --git a/doc/language/cpsc-language-overview.7.md b/doc/language/cpsc-language-overview.7.md index 93d2457..2e2a393 100644 --- a/doc/language/cpsc-language-overview.7.md +++ b/doc/language/cpsc-language-overview.7.md @@ -12,7 +12,7 @@ Assign with ASCII `<-`: `Count <- Count + 1`. Compare with `=`, `<>`, `<`, `>`, Precedence, from highest to lowest: unary operators, `* / DIV MOD`, `+ - &`, comparisons, AND, OR. Parentheses override precedence. -Parenthesized type casts, such as (C_INT)Number, have unary precedence. C_INT, C_CHAR, C_STRING and C_DOUBLE are native C aliases; TYPE name = C_TYPE("...") imports other supported C base types and pointers. Conversions between C and pseudocode types must be explicit. These are compiler extensions; see [cpsc-keyword-c_type(7)](keyword/cpsc-keyword-c_type.7.md). +Parenthesized type casts, such as (C_INT)Number, have unary precedence. C_INT, C_CHAR, C_STRING and C_DOUBLE are native C aliases; TYPE name = C_TYPE("...") imports other supported C base types and pointers. Conversions between C and pseudocode types must be explicit. These are compiler extensions; see [cpsc-keyword-c_type(7)](keyword/cpsc-keyword-c_type.7.md). EXTERN "C" declares C functions from objects named with INCLUDE_OBJ; see [cpsc-keyword-extern(7)](keyword/cpsc-keyword-extern.7.md). Literals include integers, decimal real numbers, TRUE/FALSE, strings in double quotes, and single-byte characters in single quotes. Negative numbers use unary minus. There are no string or character escape sequences. Comments begin with `//` and continue to the end of the line. @@ -46,6 +46,8 @@ Multi-file builds use the compiler extensions INCLUDE and GLOBAL. Pass every sou - [cpsc-keyword-closefile(7)](keyword/cpsc-keyword-closefile.7.md): close a text file. - [cpsc-keyword-type(7)](keyword/cpsc-keyword-type.7.md): define enumerations, pointers, and records. - [cpsc-keyword-c_type(7)](keyword/cpsc-keyword-c_type.7.md): import native C types and explicitly cast values. +- [cpsc-keyword-extern(7)](keyword/cpsc-keyword-extern.7.md): declare and call C functions. +- [cpsc-keyword-include_obj(7)](keyword/cpsc-keyword-include_obj.7.md): link a C object file or the C library. - [cpsc-keyword-delete(7)](keyword/cpsc-keyword-delete.7.md): destroy objects and release heap allocations. - [cpsc-keyword-class(7)](keyword/cpsc-keyword-class.7.md): define a class with fields and methods. - [cpsc-keyword-and(7)](keyword/cpsc-keyword-and.7.md): combine conditions with logical AND. @@ -54,7 +56,7 @@ Multi-file builds use the compiler extensions INCLUDE and GLOBAL. Pass every sou - [cpsc-keyword-div(7)](keyword/cpsc-keyword-div.7.md): perform integer division. - [cpsc-keyword-mod(7)](keyword/cpsc-keyword-mod.7.md): find the integer division remainder. -Closing and auxiliary keywords are documented with their main constructs: THEN/ELSE/ENDIF with IF; OF/OTHERWISE/ENDCASE with CASE; CATCH/ENDTRY/THROW with TRY; ENDWHILE with WHILE; UNTIL with REPEAT; TO/STEP/NEXT with FOR; RETURN/RETURNS/BYVAL/BYREF and function/procedure closing keywords with FUNCTION/PROCEDURE; READ/WRITE/APPEND with OPENFILE; ARRAY/OF and INTEGER/REAL/BOOLEAN/CHAR/STRING with DECLARE; ENDGLOBAL with GLOBAL; ENDTYPE with TYPE; PUBLIC/PRIVATE/ENDCLASS with CLASS. TRUE/FALSE are BOOLEAN literals. +Closing and auxiliary keywords are documented with their main constructs: THEN/ELSE/ENDIF with IF; OF/OTHERWISE/ENDCASE with CASE; CATCH/ENDTRY/THROW with TRY; ENDWHILE with WHILE; UNTIL with REPEAT; TO/STEP/NEXT with FOR; RETURN/RETURNS/BYVAL/BYREF and function/procedure closing keywords with FUNCTION/PROCEDURE; READ/WRITE/APPEND with OPENFILE; ARRAY/OF and INTEGER/REAL/BOOLEAN/CHAR/STRING with DECLARE; ENDGLOBAL with GLOBAL; ENDTYPE with TYPE; EXTERN_FUNCTION/EXTERN_PROCEDURE with EXTERN; PUBLIC/PRIVATE/ENDCLASS with CLASS. TRUE/FALSE are BOOLEAN literals. # BUILT-IN FUNCTIONS diff --git a/doc/language/keyword/cpsc-keyword-c_type.7.md b/doc/language/keyword/cpsc-keyword-c_type.7.md index 3951e26..4ac4141 100644 --- a/doc/language/keyword/cpsc-keyword-c_type.7.md +++ b/doc/language/keyword/cpsc-keyword-c_type.7.md @@ -17,7 +17,7 @@ C_TYPE and parenthesized casts are compiler extensions. C_TYPE requires a string Supported base types are _Bool; char, signed char and unsigned char; signed and unsigned short, int, long and long long; and float, double and long double. Legal C combinations such as unsigned, signed int and unsigned long int are accepted. Type spelling is case-sensitive. void is allowed for pointer pointees and function return types, but not variables, fields or parameters. Multiple pointer stars may precede or follow the base type: `"**char"` and `"char**"` are equivalent. Whitespace is allowed. Mixed star placement, const/volatile, structs, unions, C typedef names, array types, function pointers, complex types and compiler-specific types are unsupported. -C widths, char signedness and floating-point formats come from the compiler's build platform. Generated programs must use the same platform ABI; cross-target compilation and EXTERN declarations are not implemented. INTEGER remains signed 64-bit, CHAR remains an unsigned byte, and REAL remains double precision. In particular, C_CHAR need not have CHAR's signedness. +C widths, char signedness and floating-point formats come from the compiler's build platform. Generated programs must use the same platform ABI; cross-target compilation is not implemented. EXTERN declarations use these types to call C functions; see [cpsc-keyword-extern(7)](cpsc-keyword-extern.7.md). INTEGER remains signed 64-bit, CHAR remains an unsigned byte, and REAL remains double precision. In particular, C_CHAR need not have CHAR's signedness. A cast has unary precedence and converts its following operand. Declare a type alias before using its name in a cast. Numeric, character and boolean values can be explicitly converted between pseudocode and C types. STRING can be cast to char* and back, and C pointer types can be cast to other C pointer types. C void* and original pseudocode pointer types can be explicitly cast to each other. Pointer/integer casts and casts involving pseudocode enums or whole records are unsupported. Casts are values, not assignable locations. diff --git a/doc/language/keyword/cpsc-keyword-extern.7.md b/doc/language/keyword/cpsc-keyword-extern.7.md new file mode 100644 index 0000000..db1bde4 --- /dev/null +++ b/doc/language/keyword/cpsc-keyword-extern.7.md @@ -0,0 +1,37 @@ +% CPSC-KEYWORD-EXTERN(7) + +# NAME + +cpsc-keyword-extern, cpsc-keyword-extern_function, cpsc-keyword-extern_procedure - declare a C function + +# SYNOPSIS + +```text +EXTERN "C" EXTERN_FUNCTION name(type, ...) RETURNS type +EXTERN "C" EXTERN_PROCEDURE name(type, ...) +``` + +# DESCRIPTION + +EXTERN is a compiler extension, allowed only at top level. It declares a C function without a body; there is no ENDFUNCTION or ENDPROCEDURE. The name is the exact C symbol; calls from pseudocode remain case insensitive. Call an EXTERN_FUNCTION in an expression and an EXTERN_PROCEDURE with CALL. + +Parameters list only types. Parameter and result types must be C types: C_INT, C_CHAR, C_STRING, C_DOUBLE or a C_TYPE alias, including pointers. void is not allowed; use EXTERN_PROCEDURE for a C function returning void. EXTERN_FUNCTION requires RETURNS and EXTERN_PROCEDURE does not accept it. Structs, BYREF and BYVAL are unsupported; declare a C pointer parameter and pass ^Variable instead. Pseudocode values must be cast, as for other C values. + +A final `...` makes the function variadic. Variadic arguments must also be C values; they receive C default argument promotions (float to double, narrow integers and _Bool to int or unsigned int). + +Every EXTERN symbol must be defined by an object named with INCLUDE_OBJ, otherwise compilation fails. EXTERN cannot be combined with OVERLOAD, and a name may be declared once across all input files; put shared declarations in one file and INCLUDE it. + +# EXAMPLE + +```text +INCLUDE_OBJ "__libc" +EXTERN "C" EXTERN_FUNCTION printf(C_STRING, ...) RETURNS C_INT +EXTERN "C" EXTERN_FUNCTION abs(C_INT) RETURNS C_INT +DECLARE Count : C_INT +Count <- printf((C_STRING)"%d|", abs((C_INT)-5)) +OUTPUT "" +``` + +# SEE ALSO + +[cpsc-keyword-include_obj(7)](cpsc-keyword-include_obj.7.md), [cpsc-keyword-c_type(7)](cpsc-keyword-c_type.7.md), [cpsc-keyword-call(7)](cpsc-keyword-call.7.md), [cpsc-language-overview(7)](../cpsc-language-overview.7.md). diff --git a/doc/language/keyword/cpsc-keyword-include_obj.7.md b/doc/language/keyword/cpsc-keyword-include_obj.7.md new file mode 100644 index 0000000..1c4851a --- /dev/null +++ b/doc/language/keyword/cpsc-keyword-include_obj.7.md @@ -0,0 +1,40 @@ +% CPSC-KEYWORD-INCLUDE_OBJ(7) + +# NAME + +cpsc-keyword-include_obj - link a C object file or the C library + +# SYNOPSIS + +```text +INCLUDE_OBJ "path.o" +INCLUDE_OBJ "__libc" +``` + +# DESCRIPTION + +INCLUDE_OBJ is a compiler extension, allowed only at top level. It names an object file whose functions EXTERN declarations may call. Resolve the path relative to the including source's directory; it must name a `.o` object file built for the same platform, for example with `cc -c lib.c`. The special name `"__libc"` selects the C standard library, including the math library; it must be included explicitly before declaring C library functions. + +INCLUDE_OBJ applies to the whole program: objects from every input file are linked together, and repeated objects are linked once. Each EXTERN symbol must be defined by one of them. Native output links the objects, and `-r` loads them for execution. `-S` writes IR only; link any objects yourself when building the IR manually. + +# EXAMPLE + +`lib.c`, compiled with `cc -c lib.c`: + +```text +int add3(int a, int b, int c) { return a + b + c; } +``` + +`me.cps`: + +```text +INCLUDE_OBJ "lib.o" +EXTERN "C" EXTERN_FUNCTION add3(C_INT, C_INT, C_INT) RETURNS C_INT +OUTPUT add3((C_INT)1, (C_INT)2, (C_INT)3) +``` + +Run with `cpsc me.cps -r` to print `6`. + +# SEE ALSO + +[cpsc-keyword-extern(7)](cpsc-keyword-extern.7.md), [cpsc-keyword-include(7)](cpsc-keyword-include.7.md), [cpsc(1)](../../cpsc.1.md). -- cgit v1.3.1-4-g156e