diff options
| -rw-r--r-- | README.md | 68 | ||||
| -rw-r--r-- | plugins/regex.c | 4 | ||||
| -rw-r--r-- | src/ast/ast.h | 2 | ||||
| -rw-r--r-- | src/codegen/codegen.h | 2 | ||||
| -rw-r--r-- | src/codegen/codegen_decl.c | 9 | ||||
| -rw-r--r-- | src/codegen/codegen_main.c | 2 | ||||
| -rw-r--r-- | src/main.c | 4 | ||||
| -rw-r--r-- | src/parser/parser.h | 7 | ||||
| -rw-r--r-- | src/parser/parser_expr.c | 4 | ||||
| -rw-r--r-- | src/parser/parser_stmt.c | 54 | ||||
| -rw-r--r-- | src/parser/parser_utils.c | 32 | ||||
| -rw-r--r-- | src/utils/utils.c | 25 |
12 files changed, 176 insertions, 37 deletions
@@ -46,6 +46,14 @@ zc build hello.zc -o hello zc repl ``` +### Environment Variables + +You can set `ZC_ROOT` to specify the location of the Standard Library (standard imports like `import "std/vector.zc"`). This allows you to run `zc` from any directory. + +```bash +export ZC_ROOT=/path/to/Zen-C +``` + --- ## Language Reference @@ -238,7 +246,51 @@ unless is_valid { return; } | `?.` | Safe Navigation (`ptr?.field`) | - | | `?` | Try Operator (`res?` returns error if present) | - | -### 7. Memory Management +### 7. Printing and String Interpolation + +Zen C provides versatile options for printing to the console, including keywords and concise shorthands. + +#### Keywords + +- `print "text"`: Prints to `stdout` without a trailing newline. +- `println "text"`: Prints to `stdout` with a trailing newline. +- `eprint "text"`: Prints to `stderr` without a trailing newline. +- `eprintln "text"`: Prints to `stderr` with a trailing newline. + +#### Shorthands + +Zen C allows you to use string literals directly as statements for quick printing: + +- `"Hello World"`: Equivalent to `println "Hello World"`. (Implicitly adds newline) +- `"Hello World"..`: Equivalent to `print "Hello World"`. (No trailing newline) +- `!"Error"`: Equivalent to `eprintln "Error"`. (Output to stderr) +- `!"Error"..`: Equivalent to `eprint "Error"`. (Output to stderr, no newline) + +#### String Interpolation (F-strings) + +You can embed expressions directly into string literals using `{}` syntax. This works with all printing methods and string shorthands. + +```zc +var x = 42; +var name = "Zen"; +println "Value: {x}, Name: {name}"; +"Value: {x}, Name: {name}"; // shorthand println +``` + +#### Input Prompts (`?`) + +Zen C supports a shorthand for prompting user input using the `?` prefix. + +- `? "Prompt text"`: Prints the prompt (without newline) and waits for input (reads a line). +- `? "Enter age: " (age)`: Prints prompt and scans input into the variable `age`. + - Format specifiers are automatically inferred based on variable type. + +```c +var age = "How old are you? "; +println "You are {age} years old."; +``` + +### 8. Memory Management Zen C allows manual memory management with ergonomic aids. @@ -265,7 +317,7 @@ impl Drop for MyStruct { } ``` -### 8. Object Oriented Programming +### 9. Object Oriented Programming #### Methods Define methods on types using `impl`. @@ -305,7 +357,7 @@ struct Player { } ``` -### 9. Generics +### 10. Generics Type-safe templates for Structs and Functions. @@ -321,7 +373,7 @@ fn identity<T>(val: T) -> T { } ``` -### 10. Concurrency (Async/Await) +### 11. Concurrency (Async/Await) Built on pthreads. @@ -337,7 +389,7 @@ fn main() { } ``` -### 11. Metaprogramming +### 12. Metaprogramming #### Comptime Run code at compile-time to generate source or print messages. @@ -366,7 +418,7 @@ Pass preprocessor macros through to C. #define MAX_BUFFER 1024 ``` -### 12. Attributes +### 13. Attributes Decorate functions and structs to modify compiler behavior. @@ -386,7 +438,7 @@ Decorate functions and structs to modify compiler behavior. | `@noreturn` | Fn | Function does not return (e.g. exit). | | `@derived(...)` | Struct | Auto-implement traits (e.g. `Debug`). | -### 13. Inline Assembly +### 14. Inline Assembly Zen C provides first-class support for inline assembly, transpiling directly to GCC-style extended `asm`. @@ -435,7 +487,7 @@ fn add(a: int, b: int) -> int { > **Note:** When using Intel syntax (via `-masm=intel`), you must ensure your build is configured correctly (for example, `//> cflags: -masm=intel`). TCC does not support Intel syntax assembly. -### 14. Build Directives +### 15. Build Directives Zen C supports special comments at the top of your source file to configure the build process without needing a complex build system or Makefile. diff --git a/plugins/regex.c b/plugins/regex.c index b56515c..8fe7c67 100644 --- a/plugins/regex.c +++ b/plugins/regex.c @@ -5,7 +5,7 @@ #include <stdlib.h> #include <string.h> -static void emit_match_logic(const char *pattern, FILE *out, int *label_counter); +static void emit_match_logic(const char *pattern, FILE *out); void regex_transpile(const char *input_body, const ZApi *api) { @@ -47,7 +47,7 @@ void regex_transpile(const char *input_body, const ZApi *api) free(pattern); } -static void emit_match_logic(const char *pattern, FILE *out, int * /*label_counter*/) +static void emit_match_logic(const char *pattern, FILE *out) { const char *c = pattern; diff --git a/src/ast/ast.h b/src/ast/ast.h index 8c5bff4..1e14369 100644 --- a/src/ast/ast.h +++ b/src/ast/ast.h @@ -416,6 +416,8 @@ struct ASTNode struct { char *content; + char **used_symbols; + int used_symbol_count; } raw_stmt; struct diff --git a/src/codegen/codegen.h b/src/codegen/codegen.h index d489fb3..4b66f99 100644 --- a/src/codegen/codegen.h +++ b/src/codegen/codegen.h @@ -24,7 +24,7 @@ const char *parse_original_method_name(const char *mangled); void emit_auto_type(ParserContext *ctx, ASTNode *init_expr, Token t, FILE *out); // Declaration emission (codegen_decl.c). -void emit_preamble(FILE *out); +void emit_preamble(ParserContext *ctx, FILE *out); void emit_includes_and_aliases(ASTNode *node, FILE *out); void emit_struct_defs(ParserContext *ctx, ASTNode *node, FILE *out); void emit_trait_defs(ASTNode *node, FILE *out); diff --git a/src/codegen/codegen_decl.c b/src/codegen/codegen_decl.c index ac4ae14..4979236 100644 --- a/src/codegen/codegen_decl.c +++ b/src/codegen/codegen_decl.c @@ -8,7 +8,7 @@ #include <string.h> // Emit C preamble with standard includes and type definitions. -void emit_preamble(FILE *out) +void emit_preamble(ParserContext *ctx, FILE *out) { if (g_config.is_freestanding) { @@ -54,8 +54,11 @@ void emit_preamble(FILE *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); fputs("typedef size_t usize;\ntypedef char* string;\n", out); - fputs("#include <pthread.h>\n", out); - fputs("typedef struct { pthread_t thread; void *result; } Async;\n", out); + if (ctx->has_async) + { + fputs("#include <pthread.h>\n", out); + fputs("typedef struct { pthread_t thread; void *result; } Async;\n", out); + } fputs("typedef struct { void *func; void *ctx; } z_closure_T;\n", out); fputs("#define U0 void\n#define I8 int8_t\n#define U8 uint8_t\n#define I16 " "int16_t\n#define U16 uint16_t\n", diff --git a/src/codegen/codegen_main.c b/src/codegen/codegen_main.c index c9c69f6..3634af3 100644 --- a/src/codegen/codegen_main.c +++ b/src/codegen/codegen_main.c @@ -202,7 +202,7 @@ void codegen_node(ParserContext *ctx, ASTNode *node, FILE *out) if (!ctx->skip_preamble) { - emit_preamble(out); + emit_preamble(ctx, out); } emit_includes_and_aliases(kids, out); @@ -306,9 +306,9 @@ int main(int argc, char **argv) // TCC-specific adjustments? // Already handled by user passing --cc tcc - snprintf(cmd, sizeof(cmd), "%s %s %s %s %s -o %s out.c -lm -lpthread -I./src %s", g_config.cc, + snprintf(cmd, sizeof(cmd), "%s %s %s %s %s -o %s out.c -lm %s -I./src %s", g_config.cc, g_config.gcc_flags, g_cflags, g_config.is_freestanding ? "-ffreestanding" : "", "", - outfile, g_link_flags); + outfile, g_parser_ctx->has_async ? "-lpthread" : "", g_link_flags); if (g_config.verbose) { diff --git a/src/parser/parser.h b/src/parser/parser.h index 4aeb2c8..9cdd0d2 100644 --- a/src/parser/parser.h +++ b/src/parser/parser.h @@ -265,6 +265,7 @@ struct ParserContext FILE *hoist_out; // For plugins to hoist code to file scope int skip_preamble; // If 1, codegen_node(NODE_ROOT) won't emit preamble int is_repl; // REPL mode flag + int has_async; // Track if async features are used }; // Token helpers @@ -376,7 +377,7 @@ void init_builtins(); // Expression rewriting char *rewrite_expr_methods(ParserContext *ctx, char *raw); -char *process_fstring(ParserContext *ctx, const char *content); +char *process_fstring(ParserContext *ctx, const char *content, char ***used_syms, int *count); char *instantiate_function_template(ParserContext *ctx, const char *name, const char *concrete_type); FuncSig *find_func(ParserContext *ctx, const char *name); @@ -408,8 +409,8 @@ ASTNode *parse_guard(ParserContext *ctx, Lexer *l); ASTNode *parse_match(ParserContext *ctx, Lexer *l); ASTNode *parse_return(ParserContext *ctx, Lexer *l); -char *process_printf_sugar(ParserContext *ctx, const char *content, int newline, - const char *target); +char *process_printf_sugar(ParserContext *ctx, const char *content, int newline, const char *target, + char ***used_syms, int *count); ASTNode *parse_assert(ParserContext *ctx, Lexer *l); ASTNode *parse_defer(ParserContext *ctx, Lexer *l); ASTNode *parse_asm(ParserContext *ctx, Lexer *l); diff --git a/src/parser/parser_expr.c b/src/parser/parser_expr.c index f7af80f..9861de6 100644 --- a/src/parser/parser_expr.c +++ b/src/parser/parser_expr.c @@ -2240,7 +2240,7 @@ ASTNode *parse_expr_prec(ParserContext *ctx, Lexer *l, Precedence min_prec) } // Reuse printf sugar to generate the prompt print - char *print_code = process_printf_sugar(ctx, inner, 0, "stdout"); + char *print_code = process_printf_sugar(ctx, inner, 0, "stdout", NULL, NULL); free(inner); // Checks for (args...) suffix for SCAN mode @@ -2405,7 +2405,7 @@ ASTNode *parse_expr_prec(ParserContext *ctx, Lexer *l, Precedence min_prec) newline = 0; } - char *code = process_printf_sugar(ctx, inner, newline, "stderr"); + char *code = process_printf_sugar(ctx, inner, newline, "stderr", NULL, NULL); free(inner); ASTNode *n = ast_create(NODE_RAW_STMT); diff --git a/src/parser/parser_stmt.c b/src/parser/parser_stmt.c index 8a89cc7..484d742 100644 --- a/src/parser/parser_stmt.c +++ b/src/parser/parser_stmt.c @@ -37,6 +37,11 @@ ASTNode *parse_function(ParserContext *ctx, Lexer *l, int is_async) Token name_tok = lexer_next(l); char *name = token_strdup(name_tok); + if (is_async) + { + ctx->has_async = 1; + } + // Check for C reserved word conflict if (is_c_reserved_word(name)) { @@ -1668,7 +1673,8 @@ ASTNode *parse_for(ParserContext *ctx, Lexer *l) return n; } -char *process_printf_sugar(ParserContext *ctx, const char *content, int newline, const char *target) +char *process_printf_sugar(ParserContext *ctx, const char *content, int newline, const char *target, + char ***used_syms, int *count) { char *gen = xmalloc(8192); strcpy(gen, "({ "); @@ -1760,12 +1766,34 @@ char *process_printf_sugar(ParserContext *ctx, const char *content, int newline, clean_expr++; // Skip leading spaces } - // Mark the variable as used (fixes "Unused variable" warnings for f-string - // interpolation) - Symbol *sym = find_symbol_entry(ctx, clean_expr); - if (sym) + // Analyze usage { - sym->is_used = 1; + Lexer lex; + lexer_init(&lex, clean_expr); + Token t; + while ((t = lexer_next(&lex)).type != TOK_EOF) + { + if (t.type == TOK_IDENT) + { + char *name = token_strdup(t); + Symbol *sym = find_symbol_entry(ctx, name); + if (sym) + { + sym->is_used = 1; + } + + if (used_syms && count) + { + *used_syms = xrealloc(*used_syms, sizeof(char *) * (*count + 1)); + (*used_syms)[*count] = name; + (*count)++; + } + else + { + free(name); + } + } + } } char *type = find_symbol_type(ctx, clean_expr); @@ -2092,7 +2120,9 @@ ASTNode *parse_statement(ParserContext *ctx, Lexer *l) // ; means println (end of line), .. means print (continuation) int is_ln = (next_type == TOK_SEMICOLON); - char *code = process_printf_sugar(ctx, inner, is_ln, "stdout"); + char **used_syms = NULL; + int used_count = 0; + char *code = process_printf_sugar(ctx, inner, is_ln, "stdout", &used_syms, &used_count); if (next_type == TOK_SEMICOLON) { @@ -2109,6 +2139,8 @@ ASTNode *parse_statement(ParserContext *ctx, Lexer *l) ASTNode *n = ast_create(NODE_RAW_STMT); n->raw_stmt.content = code; + n->raw_stmt.used_symbols = used_syms; + n->raw_stmt.used_symbol_count = used_count; free(inner); return n; } @@ -2501,7 +2533,9 @@ ASTNode *parse_statement(ParserContext *ctx, Lexer *l) inner[t.len - 2] = 0; } - char *code = process_printf_sugar(ctx, inner, is_ln, target); + char **used_syms = NULL; + int used_count = 0; + char *code = process_printf_sugar(ctx, inner, is_ln, target, &used_syms, &used_count); free(inner); if (lexer_peek(l).type == TOK_SEMICOLON) @@ -2511,6 +2545,8 @@ ASTNode *parse_statement(ParserContext *ctx, Lexer *l) ASTNode *n = ast_create(NODE_RAW_STMT); n->raw_stmt.content = code; + n->raw_stmt.used_symbols = used_syms; + n->raw_stmt.used_symbol_count = used_count; return n; } } @@ -3876,7 +3912,7 @@ char *run_comptime_block(ParserContext *ctx, Lexer *l) zpanic("Could not create temp file %s", filename); } - emit_preamble(f); + emit_preamble(ctx, f); fprintf( f, "size_t _z_check_bounds(size_t index, size_t size) { if (index >= size) { fprintf(stderr, " diff --git a/src/parser/parser_utils.c b/src/parser/parser_utils.c index 7733ef7..ba506a0 100644 --- a/src/parser/parser_utils.c +++ b/src/parser/parser_utils.c @@ -1554,7 +1554,7 @@ char *instantiate_function_template(ParserContext *ctx, const char *name, const return mangled; } -char *process_fstring(ParserContext *ctx, const char *content) +char *process_fstring(ParserContext *ctx, const char *content, char ***used_syms, int *count) { (void)ctx; // suppress unused parameter warning char *gen = xmalloc(4096); @@ -1621,6 +1621,36 @@ char *process_fstring(ParserContext *ctx, const char *content) fmt = colon + 1; } + // Analyze usage in expression + { + Lexer lex; + lexer_init(&lex, expr); + Token t; + while ((t = lexer_next(&lex)).type != TOK_EOF) + { + if (t.type == TOK_IDENT) + { + char *name = token_strdup(t); + Symbol *sym = find_symbol_entry(ctx, name); + if (sym) + { + sym->is_used = 1; + } + + if (used_syms && count) + { + *used_syms = xrealloc(*used_syms, sizeof(char *) * (*count + 1)); + (*used_syms)[*count] = name; + (*count)++; + } + else + { + free(name); + } + } + } + } + if (fmt) { strcat(gen, "sprintf(_t, \"%"); diff --git a/src/utils/utils.c b/src/utils/utils.c index 2645b49..19a722e 100644 --- a/src/utils/utils.c +++ b/src/utils/utils.c @@ -561,7 +561,10 @@ void scan_build_directives(ParserContext *ctx, const char *src) if (0 == strncmp(line, "link:", 5)) { char *val = line + 5; - while (*val == ' ') val++; + while (*val == ' ') + { + val++; + } if (strlen(g_link_flags) > 0) { strcat(g_link_flags, " "); @@ -571,7 +574,10 @@ void scan_build_directives(ParserContext *ctx, const char *src) else if (0 == strncmp(line, "cflags:", 7)) { char *val = line + 7; - while (*val == ' ') val++; + while (*val == ' ') + { + val++; + } if (strlen(g_cflags) > 0) { strcat(g_cflags, " "); @@ -581,7 +587,10 @@ void scan_build_directives(ParserContext *ctx, const char *src) else if (0 == strncmp(line, "include:", 8)) { char *val = line + 8; - while (*val == ' ') val++; + while (*val == ' ') + { + val++; + } char flags[2048]; sprintf(flags, "-I%s", val); if (strlen(g_cflags) > 0) @@ -593,7 +602,10 @@ void scan_build_directives(ParserContext *ctx, const char *src) else if (strncmp(line, "lib:", 4) == 0) { char *val = line + 4; - while (*val == ' ') val++; + while (*val == ' ') + { + val++; + } char flags[2048]; sprintf(flags, "-L%s", val); if (strlen(g_link_flags) > 0) @@ -605,7 +617,10 @@ void scan_build_directives(ParserContext *ctx, const char *src) else if (strncmp(line, "define:", 7) == 0) { char *val = line + 7; - while (*val == ' ') val++; + while (*val == ' ') + { + val++; + } char flags[2048]; sprintf(flags, "-D%s", val); if (strlen(g_cflags) > 0) |
