summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--README.md35
-rw-r--r--examples/cpp_interop.zc36
-rw-r--r--src/codegen/codegen.c12
-rw-r--r--src/codegen/codegen_decl.c150
-rw-r--r--src/codegen/codegen_utils.c2
-rw-r--r--src/codegen/compat.h100
-rw-r--r--src/main.c8
-rw-r--r--src/zprep.h1
8 files changed, 298 insertions, 46 deletions
diff --git a/README.md b/README.md
index 2424fba..9a5c008 100644
--- a/README.md
+++ b/README.md
@@ -559,6 +559,41 @@ zc run app.zc --cc zig
make zig
```
+### C++ Interop
+
+Zen C can generate C++-compatible code with the `--cpp` flag, allowing seamless integration with C++ libraries.
+
+```bash
+# Direct compilation with g++
+zc app.zc --cpp
+
+# Or transpile for manual build
+zc transpile app.zc --cpp
+g++ out.c my_cpp_lib.o -o app
+```
+
+#### Using C++ in Zen C
+
+Include C++ headers and use raw blocks for C++ code:
+
+```zc
+include <vector>
+include <iostream>
+
+raw {
+ std::vector<int> make_vec(int a, int b) {
+ return {a, b};
+ }
+}
+
+fn main() {
+ var v = make_vec(1, 2);
+ raw { std::cout << "Size: " << v.size() << std::endl; }
+}
+```
+
+> **Note:** The `--cpp` flag switches the backend to `g++` and emits C++-compatible code (uses `auto` instead of `__auto_type`, function overloads instead of `_Generic`, and explicit casts for `void*`).
+
---
## Contributing
diff --git a/examples/cpp_interop.zc b/examples/cpp_interop.zc
new file mode 100644
index 0000000..2f2e033
--- /dev/null
+++ b/examples/cpp_interop.zc
@@ -0,0 +1,36 @@
+
+include <vector>
+include <iostream>
+include <string>
+
+// C++ helper functions in raw blocks
+raw {
+ std::vector<int> cpp_make_vector(int a, int b, int c) {
+ return {a, b, c};
+ }
+
+ int cpp_sum_vector(std::vector<int>& vec) {
+ int sum = 0;
+ for (int x : vec) sum += x;
+ return sum;
+ }
+
+ void cpp_print(const char* msg) {
+ std::cout << "[C++] " << msg << std::endl;
+ }
+}
+
+fn main() {
+ "=> Zen C + C++ interop.";
+
+ cpp_print("Hello from C++!");
+
+ var vec = cpp_make_vector(10, 20, 30);
+ var result = cpp_sum_vector(vec);
+ "Sum of C++ vector: {result}";
+
+ raw {
+ std::string s = "C++ string: works!";
+ std::cout << s << std::endl;
+ }
+}
diff --git a/src/codegen/codegen.c b/src/codegen/codegen.c
index 25ae5b3..1d825dc 100644
--- a/src/codegen/codegen.c
+++ b/src/codegen/codegen.c
@@ -174,7 +174,7 @@ static void codegen_match_internal(ParserContext *ctx, ASTNode *node, FILE *out,
}
else
{
- fprintf(out, "__auto_type %s = _m_%d.val; ", c->match_case.binding_name, id);
+ fprintf(out, "ZC_AUTO %s = _m_%d.val; ", c->match_case.binding_name, id);
}
}
if (is_result)
@@ -188,8 +188,7 @@ static void codegen_match_internal(ParserContext *ctx, ASTNode *node, FILE *out,
}
else
{
- fprintf(out, "__auto_type %s = _m_%d.val; ", c->match_case.binding_name,
- id);
+ fprintf(out, "ZC_AUTO %s = _m_%d.val; ", c->match_case.binding_name, id);
}
}
else
@@ -201,8 +200,7 @@ static void codegen_match_internal(ParserContext *ctx, ASTNode *node, FILE *out,
}
else
{
- fprintf(out, "__auto_type %s = _m_%d.err; ", c->match_case.binding_name,
- id);
+ fprintf(out, "ZC_AUTO %s = _m_%d.err; ", c->match_case.binding_name, id);
}
}
}
@@ -217,7 +215,7 @@ static void codegen_match_internal(ParserContext *ctx, ASTNode *node, FILE *out,
{
f = c->match_case.pattern;
}
- fprintf(out, "__auto_type %s = _m_%d.data.%s; ", c->match_case.binding_name, id, f);
+ fprintf(out, "ZC_AUTO %s = _m_%d.data.%s; ", c->match_case.binding_name, id, f);
}
}
@@ -1838,7 +1836,7 @@ void codegen_node_single(ParserContext *ctx, ASTNode *node, FILE *out)
}
else
{
- fprintf(out, "__auto_type %s = ", node->for_range.var_name);
+ fprintf(out, "ZC_AUTO %s = ", node->for_range.var_name);
}
codegen_expression(ctx, node->for_range.start, out);
fprintf(out, "; %s < ", node->for_range.var_name);
diff --git a/src/codegen/codegen_decl.c b/src/codegen/codegen_decl.c
index 11970a5..e42d83b 100644
--- a/src/codegen/codegen_decl.c
+++ b/src/codegen/codegen_decl.c
@@ -52,7 +52,43 @@ void emit_preamble(ParserContext *ctx, FILE *out)
out);
fputs("#include <stdarg.h>\n#include <stdint.h>\n#include <stdbool.h>\n", out);
fputs("#include <unistd.h>\n#include <fcntl.h>\n", out); // POSIX functions
- fputs("#ifdef __TINYC__\n#define __auto_type __typeof__\n#endif\n", out);
+
+ // C++ compatibility
+ if (g_config.use_cpp)
+ {
+ // For C++: define ZC_AUTO as auto, include compat.h macros inline
+ fputs("#define ZC_AUTO auto\n", out);
+ fputs("#define ZC_CAST(T, x) static_cast<T>(x)\n", out);
+ // C++ _z_str via overloads
+ fputs("inline const char* _z_str(bool) { return \"%d\"; }\n", out);
+ fputs("inline const char* _z_str(char) { return \"%c\"; }\n", out);
+ fputs("inline const char* _z_str(int) { return \"%d\"; }\n", out);
+ fputs("inline const char* _z_str(unsigned int) { return \"%u\"; }\n", out);
+ fputs("inline const char* _z_str(long) { return \"%ld\"; }\n", out);
+ fputs("inline const char* _z_str(unsigned long) { return \"%lu\"; }\n", out);
+ fputs("inline const char* _z_str(long long) { return \"%lld\"; }\n", out);
+ fputs("inline const char* _z_str(unsigned long long) { return \"%llu\"; }\n", out);
+ fputs("inline const char* _z_str(float) { return \"%f\"; }\n", out);
+ fputs("inline const char* _z_str(double) { return \"%f\"; }\n", out);
+ fputs("inline const char* _z_str(char*) { return \"%s\"; }\n", out);
+ fputs("inline const char* _z_str(const char*) { return \"%s\"; }\n", out);
+ fputs("inline const char* _z_str(void*) { return \"%p\"; }\n", out);
+ }
+ else
+ {
+ // C mode
+ fputs("#define ZC_AUTO __auto_type\n", out);
+ fputs("#define ZC_CAST(T, x) ((T)(x))\n", out);
+ fputs("#ifdef __TINYC__\n#define __auto_type __typeof__\n#endif\n", out);
+ fputs("#define _z_str(x) _Generic((x), _Bool: \"%d\", char: \"%c\", "
+ "signed char: \"%c\", unsigned char: \"%u\", short: \"%d\", "
+ "unsigned short: \"%u\", int: \"%d\", unsigned int: \"%u\", "
+ "long: \"%ld\", unsigned long: \"%lu\", long long: \"%lld\", "
+ "unsigned long long: \"%llu\", float: \"%f\", double: \"%f\", "
+ "char*: \"%s\", void*: \"%p\")\n",
+ out);
+ }
+
fputs("typedef size_t usize;\ntypedef char* string;\n", out);
if (ctx->has_async)
{
@@ -68,19 +104,19 @@ void emit_preamble(ParserContext *ctx, FILE *out)
"uint64_t\n",
out);
fputs("#define F32 float\n#define F64 double\n", out);
- fputs("#define _z_str(x) _Generic((x), _Bool: \"%d\", char: \"%c\", "
- "signed char: \"%c\", unsigned char: \"%u\", short: \"%d\", "
- "unsigned short: \"%u\", int: \"%d\", unsigned int: \"%u\", "
- "long: \"%ld\", unsigned long: \"%lu\", long long: \"%lld\", "
- "unsigned long long: \"%llu\", float: \"%f\", double: \"%f\", "
- "char*: \"%s\", void*: \"%p\")\n",
- out);
// Memory Mapping.
- fputs("#define z_malloc malloc\n#define z_realloc realloc\n#define z_free "
- "free\n#define "
- "z_print printf\n",
- out);
+ if (g_config.use_cpp)
+ {
+ // C++ needs explicit casts for void* conversions
+ fputs("#define z_malloc(sz) static_cast<char*>(malloc(sz))\n", out);
+ fputs("#define z_realloc(p, sz) static_cast<char*>(realloc(p, sz))\n", out);
+ }
+ else
+ {
+ fputs("#define z_malloc malloc\n#define z_realloc realloc\n", out);
+ }
+ fputs("#define z_free free\n#define z_print printf\n", out);
fputs("void z_panic(const char* msg) { fprintf(stderr, \"Panic: %s\\n\", "
"msg); exit(1); }\n",
out);
@@ -93,19 +129,41 @@ void emit_preamble(ParserContext *ctx, FILE *out)
"\"Assertion failed: \" "
"__VA_ARGS__); exit(1); }\n",
out);
- fputs("string _z_readln_raw() { "
- "size_t cap = 64; size_t len = 0; "
- "char *line = z_malloc(cap); "
- "if(!line) return NULL; "
- "int c; "
- "while((c = fgetc(stdin)) != EOF) { "
- "if(c == '\\n') break; "
- "if(len + 1 >= cap) { cap *= 2; char *n = z_realloc(line, cap); "
- "if(!n) { z_free(line); return NULL; } line = n; } "
- "line[len++] = c; } "
- "if(len == 0 && c == EOF) { z_free(line); return NULL; } "
- "line[len] = 0; return line; }\n",
- out);
+
+ // C++ compatible readln helper
+ if (g_config.use_cpp)
+ {
+ fputs(
+ "string _z_readln_raw() { "
+ "size_t cap = 64; size_t len = 0; "
+ "char *line = static_cast<char*>(malloc(cap)); "
+ "if(!line) return NULL; "
+ "int c; "
+ "while((c = fgetc(stdin)) != EOF) { "
+ "if(c == '\\n') break; "
+ "if(len + 1 >= cap) { cap *= 2; char *n = static_cast<char*>(realloc(line, cap)); "
+ "if(!n) { free(line); return NULL; } line = n; } "
+ "line[len++] = c; } "
+ "if(len == 0 && c == EOF) { free(line); return NULL; } "
+ "line[len] = 0; return line; }\n",
+ out);
+ }
+ else
+ {
+ fputs("string _z_readln_raw() { "
+ "size_t cap = 64; size_t len = 0; "
+ "char *line = z_malloc(cap); "
+ "if(!line) return NULL; "
+ "int c; "
+ "while((c = fgetc(stdin)) != EOF) { "
+ "if(c == '\\n') break; "
+ "if(len + 1 >= cap) { cap *= 2; char *n = z_realloc(line, cap); "
+ "if(!n) { z_free(line); return NULL; } line = n; } "
+ "line[len++] = c; } "
+ "if(len == 0 && c == EOF) { z_free(line); return NULL; } "
+ "line[len] = 0; return line; }\n",
+ out);
+ }
fputs("int _z_scan_helper(const char *fmt, ...) { char *l = "
"_z_readln_raw(); if(!l) return "
"0; va_list ap; va_start(ap, fmt); int r = vsscanf(l, fmt, ap); "
@@ -809,27 +867,45 @@ void print_type_defs(ParserContext *ctx, FILE *out, ASTNode *nodes)
fprintf(out, "typedef struct { void **data; int len; int cap; } Vec;\n");
fprintf(out, "#define Vec_new() (Vec){.data=0, .len=0, .cap=0}\n");
- fprintf(out, "void _z_vec_push(Vec *v, void *item) { if(v->len >= v->cap) { "
- "v->cap = v->cap?v->cap*2:8; "
- "v->data = z_realloc(v->data, v->cap * sizeof(void*)); } "
- "v->data[v->len++] = item; }\n");
+
+ if (g_config.use_cpp)
+ {
+ fprintf(out, "void _z_vec_push(Vec *v, void *item) { if(v->len >= v->cap) { "
+ "v->cap = v->cap?v->cap*2:8; "
+ "v->data = static_cast<void**>(realloc(v->data, v->cap * sizeof(void*))); } "
+ "v->data[v->len++] = item; }\n");
+ fprintf(out, "static inline Vec _z_make_vec(int count, ...) { Vec v = {0}; v.cap = "
+ "count > 8 ? "
+ "count : 8; v.data = static_cast<void**>(malloc(v.cap * sizeof(void*))); "
+ "v.len = 0; va_list "
+ "args; "
+ "va_start(args, count); for(int i=0; i<count; i++) { v.data[v.len++] = "
+ "va_arg(args, void*); } va_end(args); return v; }\n");
+ }
+ else
+ {
+ fprintf(out, "void _z_vec_push(Vec *v, void *item) { if(v->len >= v->cap) { "
+ "v->cap = v->cap?v->cap*2:8; "
+ "v->data = z_realloc(v->data, v->cap * sizeof(void*)); } "
+ "v->data[v->len++] = item; }\n");
+ fprintf(out, "static inline Vec _z_make_vec(int count, ...) { Vec v = {0}; v.cap = "
+ "count > 8 ? "
+ "count : 8; v.data = z_malloc(v.cap * sizeof(void*)); v.len = 0; va_list "
+ "args; "
+ "va_start(args, count); for(int i=0; i<count; i++) { v.data[v.len++] = "
+ "va_arg(args, void*); } va_end(args); return v; }\n");
+ }
fprintf(out, "#define Vec_push(v, i) _z_vec_push(&(v), (void*)(long)(i))\n");
- fprintf(out, "static inline Vec _z_make_vec(int count, ...) { Vec v = {0}; v.cap = "
- "count > 8 ? "
- "count : 8; v.data = z_malloc(v.cap * sizeof(void*)); v.len = 0; va_list "
- "args; "
- "va_start(args, count); for(int i=0; i<count; i++) { v.data[v.len++] = "
- "va_arg(args, void*); } va_end(args); return v; }\n");
if (g_config.is_freestanding)
{
- fprintf(out, "#define _z_check_bounds(index, limit) ({ __auto_type _i = "
+ fprintf(out, "#define _z_check_bounds(index, limit) ({ ZC_AUTO _i = "
"(index); if(_i < 0 "
"|| _i >= (limit)) { z_panic(\"index out of bounds\"); } _i; })\n");
}
else
{
- fprintf(out, "#define _z_check_bounds(index, limit) ({ __auto_type _i = "
+ fprintf(out, "#define _z_check_bounds(index, limit) ({ ZC_AUTO _i = "
"(index); if(_i < 0 "
"|| _i >= (limit)) { fprintf(stderr, \"Index out of bounds: "
"%%ld (limit "
diff --git a/src/codegen/codegen_utils.c b/src/codegen/codegen_utils.c
index 392557f..b1fcf4c 100644
--- a/src/codegen/codegen_utils.c
+++ b/src/codegen/codegen_utils.c
@@ -515,7 +515,7 @@ void emit_auto_type(ParserContext *ctx, ASTNode *init_expr, Token t, FILE *out)
}
else
{
- fprintf(out, "__auto_type");
+ fprintf(out, "ZC_AUTO");
}
}
}
diff --git a/src/codegen/compat.h b/src/codegen/compat.h
new file mode 100644
index 0000000..423b1d9
--- /dev/null
+++ b/src/codegen/compat.h
@@ -0,0 +1,100 @@
+
+#ifndef ZC_COMPAT_H
+#define ZC_COMPAT_H
+
+#ifdef __cplusplus
+/* C++ mode */
+#define ZC_AUTO auto
+#define ZC_CAST(T, x) static_cast<T>(x)
+#define ZC_REINTERPRET(T, x) reinterpret_cast<T>(x)
+#define ZC_EXTERN_C extern "C"
+#define ZC_EXTERN_C_BEGIN \
+ extern "C" \
+ {
+#define ZC_EXTERN_C_END }
+#else
+/* C mode */
+#define ZC_AUTO __auto_type
+#define ZC_CAST(T, x) ((T)(x))
+#define ZC_REINTERPRET(T, x) ((T)(x))
+#define ZC_EXTERN_C
+#define ZC_EXTERN_C_BEGIN
+#define ZC_EXTERN_C_END
+#endif
+
+#ifdef __cplusplus
+#include <type_traits>
+
+inline const char *_zc_fmt(bool)
+{
+ return "%d";
+}
+inline const char *_zc_fmt(char)
+{
+ return "%c";
+}
+inline const char *_zc_fmt(signed char)
+{
+ return "%c";
+}
+inline const char *_zc_fmt(unsigned char)
+{
+ return "%u";
+}
+inline const char *_zc_fmt(short)
+{
+ return "%d";
+}
+inline const char *_zc_fmt(unsigned short)
+{
+ return "%u";
+}
+inline const char *_zc_fmt(int)
+{
+ return "%d";
+}
+inline const char *_zc_fmt(unsigned int)
+{
+ return "%u";
+}
+inline const char *_zc_fmt(long)
+{
+ return "%ld";
+}
+inline const char *_zc_fmt(unsigned long)
+{
+ return "%lu";
+}
+inline const char *_zc_fmt(long long)
+{
+ return "%lld";
+}
+inline const char *_zc_fmt(unsigned long long)
+{
+ return "%llu";
+}
+inline const char *_zc_fmt(float)
+{
+ return "%f";
+}
+inline const char *_zc_fmt(double)
+{
+ return "%f";
+}
+inline const char *_zc_fmt(char *)
+{
+ return "%s";
+}
+inline const char *_zc_fmt(const char *)
+{
+ return "%s";
+}
+inline const char *_zc_fmt(void *)
+{
+ return "%p";
+}
+
+#define _z_str(x) _zc_fmt(x)
+#endif
+
+#endif
diff --git a/src/main.c b/src/main.c
index 4c91330..edc3723 100644
--- a/src/main.c
+++ b/src/main.c
@@ -37,7 +37,7 @@ void print_usage()
printf(" transpile Transpile to C code only (no compilation)\n");
printf(" lsp Start Language Server\n");
printf("Options:\n");
- printf(" --version Print version information");
+ printf(" --version Print version information\n");
printf(" -o <file> Output executable name\n");
printf(" --emit-c Keep generated C file (out.c)\n");
printf(" --freestanding Freestanding mode (no stdlib)\n");
@@ -47,6 +47,7 @@ void print_usage()
printf(" -v, --verbose Verbose output\n");
printf(" -q, --quiet Quiet output\n");
printf(" -c Compile only (produce .o)\n");
+ printf(" --cpp Use C++ mode.\n");
}
int main(int argc, char **argv)
@@ -141,6 +142,11 @@ int main(int argc, char **argv)
{
g_config.is_freestanding = 1;
}
+ else if (strcmp(arg, "--cpp") == 0)
+ {
+ strcpy(g_config.cc, "g++");
+ g_config.use_cpp = 1;
+ }
else if (strcmp(arg, "--check") == 0)
{
g_config.mode_check = 1;
diff --git a/src/zprep.h b/src/zprep.h
index bf770a3..8a4ba3e 100644
--- a/src/zprep.h
+++ b/src/zprep.h
@@ -179,6 +179,7 @@ typedef struct
int repl_mode; // 1 if --repl (internal flag for REPL usage).
int is_freestanding; // 1 if --freestanding.
int mode_transpile; // 1 if 'transpile' command.
+ int use_cpp; // 1 if --cpp (emit C++ compatible code).
// GCC Flags accumulator.
char gcc_flags[4096];