diff options
| -rw-r--r-- | examples/tools/mini_grep.zc | 311 | ||||
| -rw-r--r-- | src/ast/ast.h | 4 | ||||
| -rw-r--r-- | src/codegen/codegen.c | 90 | ||||
| -rw-r--r-- | src/codegen/codegen_utils.c | 36 | ||||
| -rw-r--r-- | src/lexer/token.c | 10 | ||||
| -rw-r--r-- | src/main.c | 15 | ||||
| -rw-r--r-- | src/parser/parser_expr.c | 42 | ||||
| -rw-r--r-- | src/parser/parser_stmt.c | 23 | ||||
| -rw-r--r-- | src/parser/parser_utils.c | 5 | ||||
| -rw-r--r-- | src/zprep.h | 7 | ||||
| -rw-r--r-- | std/string.zc | 4 |
11 files changed, 496 insertions, 51 deletions
diff --git a/examples/tools/mini_grep.zc b/examples/tools/mini_grep.zc new file mode 100644 index 0000000..827b73a --- /dev/null +++ b/examples/tools/mini_grep.zc @@ -0,0 +1,311 @@ + +import "std.zc" + +struct GrepConfig { + query: string; + path: string; + ignore_case: bool; + line_numbers: bool; + recursive: bool; + invert: bool; + color: bool; +} + +fn to_lower(c: char) -> char +{ + if (c >= 'A' and c <= 'Z') return c + 32; + return c; +} + +fn str_find_case(haystack: string, needle: string, ignore_case: bool) -> Option<string> +{ + if (!ignore_case) + { + var res = (string)strstr(haystack, needle); + if (res != NULL) return Option<string>::Some(res); + return Option<string>::None(); + } + + var h = haystack; + var n = needle; + var n_len = strlen(needle); + + while (*h != 0) + { + var is_match: bool = true; + for i in 0..n_len + { + var hc = to_lower(*(h + i)); + var nc = to_lower(*(n + i)); + if (hc != nc) + { + is_match = false; + break; + } + } + if (is_match) + { + return Option<string>::Some(h); + } + h++; + } + return Option<string>::None(); +} + +fn print_highlight(line: string, match_ptr: string, match_len: usize, config: GrepConfig) { + if (!config.color) + { + "{line}"; + return; + } + + var current = line; + while (current < match_ptr) + { + "{*current}"..; + current++; + } + + if (config.color) + { + "\x1b[31m"..; + } + + for i in 0..match_len + { + "{*(match_ptr + i)}"..; + } + if (config.color) + { + "\x1b[0m"..; + } + + current = match_ptr + match_len; + "{current}"; +} + +fn grep_file(path: string, config: GrepConfig) -> Result<int> { + var content_str: String = File::read_all(path)?; + defer content_str.destroy(); + var content = content_str.c_str(); + + var line_num = 1; + var ptr = content; + var line_start = content; + + var q_len = strlen(config.query); + + while (*ptr != 0) + { + if (*ptr == '\n') + { + *ptr = 0; + + var match_opt = str_find_case(line_start, config.query, config.ignore_case); + var found_str = ""; + if (match_opt.is_some()) found_str = match_opt.unwrap(); + + if ((match_opt.is_some() and !config.invert) || (!match_opt.is_some() and config.invert)) + { + if (config.path) + { + if (config.color) + "\x1b[35m{path}\x1b[0m:"..; + else + "{path}:"..; + } + + if (config.line_numbers) + { + if (config.color) + "\x1b[32m{line_num}\x1b[0m:"..; + else + "{line_num}:"..; + } + + if (match_opt.is_some()) + { + print_highlight(line_start, found_str, q_len, config); + } + else + { + "{line_start}"; + } + } + + *ptr = '\n'; + line_start = ptr + 1; + line_num = line_num + 1; + } + ptr = ptr + 1; + } + + if (ptr > line_start) + { + var match_opt = str_find_case(line_start, config.query, config.ignore_case); + var found_str = ""; + if (match_opt.is_some()) found_str = match_opt.unwrap(); + + if ((match_opt.is_some() and !config.invert) || (!match_opt.is_some() and config.invert)) + { + if (config.path) "{path}:"..; + if (config.line_numbers) "{line_num}:"..; + if (match_opt.is_some()) + { + print_highlight(line_start, found_str, q_len, config); + } + else + { + "{line_start}"; + } + } + } + return Result<int>::Ok(0); +} + +fn visit_dir(path: string, config: GrepConfig) { + var entries_res = File::read_dir(path); + guard entries_res.is_ok() else return; + + var entries = entries_res.unwrap(); + var p_base = Path::new(path); + + for i in 0..entries.length() + { + var entry: DirEntry = entries.get(i); + var full_path_obj = p_base.join(entry.name.c_str()); + var full_path = full_path_obj.c_str(); + + if (entry.is_dir) + { + if (config.recursive) + { + visit_dir(full_path, config); + } + } + else + { + grep_file(full_path, config); + } + } +} + +fn print_usage() +{ + "Usage: mini_grep [OPTIONS] <QUERY> <PATH>"; + "Options:"; + " -i Ignore case"; + " -n Show line numbers"; + " -v Invert match"; + " -r Recursive search"; + " --color Enable color output"; +} + +fn main(argc: int, argv: string*) +{ + if (argc < 3) + { + print_usage(); + return 0; + } + + var config = GrepConfig { + query: NULL, + path: NULL, + ignore_case: false, + line_numbers: false, + recursive: false, + invert: false, + color: false + }; + + var arg_idx = 1; + while (arg_idx < argc) + { + var arg = argv[arg_idx]; + if (arg[0] == '-') + { + if (arg[1] == '-') + { + if (strcmp(arg, "--color") == 0) + { + config.color = true; + } + else + { + "Unknown option: {arg}"; + print_usage(); + return 0; + } + } + else + { + var len = strlen(arg); + for i in 1..len + { + var c = arg[i]; + match c { + 'i' => config.ignore_case = true, + 'n' => config.line_numbers = true, + 'v' => config.invert = true, + 'r' => config.recursive = true, + _ => { + !"Unknown option flag: '{c}' in {arg}"; + print_usage(); + exit(1); + } + } + } + } + } + else + { + if (config.query == NULL) config.query = arg; + else if (config.path == NULL) config.path = arg; + } + arg_idx = arg_idx + 1; + } + + if (config.query == NULL or config.path == NULL) + { + print_usage(); + return 0; + } + + + if (File::exists(config.path)) + { + var meta_res = File::metadata(config.path); + if (meta_res.is_err()) + { + !"grep: {config.path}: Error reading metadata"; + return 1; + } + var meta = meta_res.unwrap(); + + if (meta.is_dir) + { + if (config.recursive) + { + visit_dir(config.path, config); + } + else + { + "grep: {config.path}: Is a directory"; + } + } + else + { + var res = grep_file(config.path, config); + if (res.is_err()) { + !"Error: {res.err}"; + return 1; + } + } + } + else + { + "grep: {config.path}: No such file or directory"; + } + return 0; +} diff --git a/src/ast/ast.h b/src/ast/ast.h index 0da2c0d..e22b35c 100644 --- a/src/ast/ast.h +++ b/src/ast/ast.h @@ -242,8 +242,8 @@ struct ASTNode struct { char *var_name; - char *start; - char *end; + ASTNode *start; + ASTNode *end; char *step; ASTNode *body; } for_range; diff --git a/src/codegen/codegen.c b/src/codegen/codegen.c index 1592806..17b0816 100644 --- a/src/codegen/codegen.c +++ b/src/codegen/codegen.c @@ -11,14 +11,15 @@ #include "zprep_plugin.h" // static function for internal use. -static void codegen_match_internal(ParserContext *ctx, ASTNode *node, FILE *out) +static char *g_current_func_ret_type = NULL; +static void codegen_match_internal(ParserContext *ctx, ASTNode *node, FILE *out, int use_result) { int id = tmp_counter++; int is_self = (node->match_stmt.expr->type == NODE_EXPR_VAR && strcmp(node->match_stmt.expr->var_ref.name, "self") == 0); char *ret_type = infer_type(ctx, node); - int is_expr = (ret_type && strcmp(ret_type, "void") != 0); + int is_expr = (use_result && ret_type && strcmp(ret_type, "void") != 0); fprintf(out, "({ "); emit_auto_type(ctx, node->match_stmt.expr, node->token, out); @@ -151,6 +152,11 @@ static void codegen_match_internal(ParserContext *ctx, ASTNode *node, FILE *out) // Numeric pattern fprintf(out, "_m_%d == %s", id, c->match_case.pattern); } + else if (c->match_case.pattern[0] == '\'') + { + // Char literal pattern + fprintf(out, "_m_%d == %s", id, c->match_case.pattern); + } else { fprintf(out, "1"); @@ -290,7 +296,7 @@ void codegen_expression(ParserContext *ctx, ASTNode *node, FILE *out) switch (node->type) { case NODE_MATCH: - codegen_match_internal(ctx, node, out); + codegen_match_internal(ctx, node, out, 1); break; case NODE_EXPR_BINARY: if (strncmp(node->binary.op, "??", 2) == 0 && strlen(node->binary.op) == 2) @@ -848,13 +854,13 @@ void codegen_expression(ParserContext *ctx, ASTNode *node, FILE *out) case NODE_TRY: { char *type_name = "Result"; - if (node->try_stmt.expr->type_info && node->try_stmt.expr->type_info->name) + if (g_current_func_ret_type) { - type_name = node->try_stmt.expr->type_info->name; + type_name = g_current_func_ret_type; } - else if (node->try_stmt.expr->resolved_type) + else if (node->try_stmt.expr->type_info && node->try_stmt.expr->type_info->name) { - type_name = node->try_stmt.expr->resolved_type; + type_name = node->try_stmt.expr->type_info->name; } if (strcmp(type_name, "__auto_type") == 0 || strcmp(type_name, "unknown") == 0) @@ -862,14 +868,57 @@ void codegen_expression(ParserContext *ctx, ASTNode *node, FILE *out) type_name = "Result"; } + char *search_name = type_name; + if (strncmp(search_name, "struct ", 7) == 0) + { + search_name += 7; + } + + int is_enum = 0; + StructRef *er = ctx->parsed_enums_list; + while (er) + { + if (er->node && er->node->type == NODE_ENUM && + strcmp(er->node->enm.name, search_name) == 0) + { + is_enum = 1; + break; + } + er = er->next; + } + if (!is_enum) + { + ASTNode *ins = ctx->instantiated_structs; + while (ins) + { + if (ins->type == NODE_ENUM && strcmp(ins->enm.name, search_name) == 0) + { + is_enum = 1; + break; + } + ins = ins->next; + } + } + fprintf(out, "({ "); emit_auto_type(ctx, node->try_stmt.expr, node->token, out); fprintf(out, " _try = "); codegen_expression(ctx, node->try_stmt.expr, out); - fprintf(out, - "; if (_try.tag == %s_Err_Tag) return (%s_Err(_try.data.Err)); " - "_try.data.Ok; })", - type_name, type_name); + + if (is_enum) + { + fprintf(out, + "; if (_try.tag == %s_Err_Tag) return (%s_Err(_try.data.Err)); " + "_try.data.Ok; })", + search_name, search_name); + } + else + { + fprintf(out, + "; if (!_try.is_ok) return %s_Err(_try.err); " + "_try.val; })", + search_name); + } break; } case NODE_RAW_STMT: @@ -1149,6 +1198,10 @@ void codegen_node_single(ParserContext *ctx, ASTNode *node, FILE *out) } switch (node->type) { + case NODE_MATCH: + codegen_match_internal(ctx, node, out, 0); // 0 = statement context + fprintf(out, ";\n"); + break; case NODE_FUNCTION: if (!node->func.body) { @@ -1320,11 +1373,14 @@ void codegen_node_single(ParserContext *ctx, ASTNode *node, FILE *out) } fprintf(out, "%s %s(%s)\n", node->func.ret_type, node->func.name, node->func.args); fprintf(out, "{\n"); + char *prev_ret = g_current_func_ret_type; + g_current_func_ret_type = node->func.ret_type; codegen_walker(ctx, node->func.body, out); for (int i = defer_count - 1; i >= 0; i--) { codegen_node_single(ctx, defer_stack[i], out); } + g_current_func_ret_type = prev_ret; fprintf(out, "}\n"); break; @@ -1500,8 +1556,6 @@ void codegen_node_single(ParserContext *ctx, ASTNode *node, FILE *out) if (node->var_decl.init_expr) { inferred = infer_type(ctx, node->var_decl.init_expr); - fprintf(stderr, "DEBUG: var '%s' inferred = '%s'\n", node->var_decl.name, - inferred ? inferred : "(null)"); } if (inferred && strcmp(inferred, "__auto_type") != 0) @@ -1682,14 +1736,18 @@ void codegen_node_single(ParserContext *ctx, ASTNode *node, FILE *out) fprintf(out, "for ("); if (strstr(g_config.cc, "tcc")) { - fprintf(out, "__typeof__((%s)) %s = ", node->for_range.start, node->for_range.var_name); + fprintf(out, "__typeof__(("); + codegen_expression(ctx, node->for_range.start, out); + fprintf(out, ")) %s = ", node->for_range.var_name); } else { fprintf(out, "__auto_type %s = ", node->for_range.var_name); } - fprintf(out, "%s; %s < %s; %s", node->for_range.start, node->for_range.var_name, - node->for_range.end, node->for_range.var_name); + codegen_expression(ctx, node->for_range.start, out); + fprintf(out, "; %s < ", node->for_range.var_name); + codegen_expression(ctx, node->for_range.end, out); + fprintf(out, "; %s", node->for_range.var_name); if (node->for_range.step) { fprintf(out, " += %s) ", node->for_range.step); diff --git a/src/codegen/codegen_utils.c b/src/codegen/codegen_utils.c index 5dcbf19..8969276 100644 --- a/src/codegen/codegen_utils.c +++ b/src/codegen/codegen_utils.c @@ -194,26 +194,38 @@ char *infer_type(ParserContext *ctx, ASTNode *node) } } - // Check if it's a function pointer or built-in registered as a symbol. if (node->call.callee->type == NODE_EXPR_VAR) { Symbol *sym = find_symbol_entry(ctx, node->call.callee->var_ref.name); - if (sym && sym->type_info) + if (sym && sym->type_info && sym->type_info->kind == TYPE_FUNCTION && + sym->type_info->inner) { - // If it's a function pointer type, return return type. - if (sym->type_info->kind == TYPE_FUNCTION && sym->type_info->inner) - { - return type_to_string(sym->type_info->inner); - } - if (sym->type_info->kind == TYPE_POINTER && sym->type_info->inner && - sym->type_info->inner->kind == TYPE_FUNCTION && sym->type_info->inner->inner) + return type_to_string(sym->type_info->inner); + } + } + } + + if (node->type == NODE_TRY) + { + char *inner_type = infer_type(ctx, node->try_stmt.expr); + if (inner_type) + { + // Extract T from Result<T> or Option<T> + char *start = strchr(inner_type, '<'); + if (start) + { + start++; // Skip < + char *end = strrchr(inner_type, '>'); + if (end && end > start) { - return type_to_string(sym->type_info->inner->inner); + int len = end - start; + char *extracted = xmalloc(len + 1); + strncpy(extracted, start, len); + extracted[len] = 0; + return extracted; } } } - - return NULL; } if (node->type == NODE_EXPR_MEMBER) diff --git a/src/lexer/token.c b/src/lexer/token.c index ebed001..6ada798 100644 --- a/src/lexer/token.c +++ b/src/lexer/token.c @@ -79,7 +79,7 @@ Token lexer_next(Lexer *l) } l->pos += len; l->col += len; - return (Token){TOK_COMMENT, s, len, start_line, start_col}; + return lexer_next(l); } // Identifiers. @@ -150,6 +150,14 @@ Token lexer_next(Lexer *l) { return (Token){TOK_AWAIT, s, 5, start_line, start_col}; } + if (len == 3 && strncmp(s, "and", 3) == 0) + { + return (Token){TOK_AND, s, 3, start_line, start_col}; + } + if (len == 2 && strncmp(s, "or", 2) == 0) + { + return (Token){TOK_OR, s, 2, start_line, start_col}; + } // F-Strings if (len == 1 && s[0] == 'f' && s[1] == '"') @@ -192,6 +192,13 @@ int main(int argc, char **argv) // Initialize Plugin Manager zptr_plugin_mgr_init(); + // Parse context init + ParserContext ctx; + memset(&ctx, 0, sizeof(ctx)); + + // Scan for build directives (e.g. //> link: -lm) + scan_build_directives(&ctx, src); + // Register built-in plugins extern ZPlugin brainfuck_plugin; extern ZPlugin befunge_plugin; @@ -210,9 +217,6 @@ int main(int argc, char **argv) Lexer l; lexer_init(&l, src); - // Parse - ParserContext ctx; - memset(&ctx, 0, sizeof(ctx)); ctx.hoist_out = tmpfile(); // Temp file for plugin hoisting if (!ctx.hoist_out) { @@ -262,8 +266,9 @@ int main(int argc, char **argv) // TCC-specific adjustments? // Already handled by user passing --cc tcc - snprintf(cmd, sizeof(cmd), "%s %s %s -o %s out.c -lm -lpthread -I./src", g_config.cc, - g_config.gcc_flags, g_config.is_freestanding ? "-ffreestanding" : "", outfile); + snprintf(cmd, sizeof(cmd), "%s %s %s %s %s -o %s out.c -lm -lpthread -I./src %s", g_config.cc, + g_config.gcc_flags, g_cflags, g_config.is_freestanding ? "-ffreestanding" : "", "", + outfile, g_link_flags); if (g_config.verbose) { diff --git a/src/parser/parser_expr.c b/src/parser/parser_expr.c index 469d623..b451a26 100644 --- a/src/parser/parser_expr.c +++ b/src/parser/parser_expr.c @@ -245,6 +245,16 @@ Precedence get_token_precedence(Token t) return PREC_OR; } + if (t.type == TOK_OR) + { + return PREC_OR; + } + + if (t.type == TOK_AND) + { + return PREC_AND; + } + if (t.type == TOK_QQ_EQ) { return PREC_ASSIGNMENT; @@ -1247,6 +1257,7 @@ ASTNode *parse_primary(ParserContext *ctx, Lexer *l) free(acc); acc = tmp; } + else { char *tmp = xmalloc(strlen(mod->base_name) + suffix.len + 2); @@ -3096,6 +3107,14 @@ ASTNode *parse_expr_prec(ParserContext *ctx, Lexer *l, Precedence min_prec) { bin->binary.op = xstrdup(">"); } + else if (op.type == TOK_AND) + { + bin->binary.op = xstrdup("&&"); + } + else if (op.type == TOK_OR) + { + bin->binary.op = xstrdup("||"); + } else { bin->binary.op = token_strdup(op); @@ -3465,6 +3484,27 @@ ASTNode *parse_expr_prec(ParserContext *ctx, Lexer *l, Precedence min_prec) char *t2 = type_to_string(rhs->type_info); // Skip type check if either operand is void* (escape hatch type) int skip_check = (strcmp(t1, "void*") == 0 || strcmp(t2, "void*") == 0); + + // Allow comparing pointers/strings with integer literal 0 (NULL) + if (!skip_check) + { + int lhs_is_ptr = + (lhs->type_info->kind == TYPE_POINTER || + lhs->type_info->kind == TYPE_STRING || (t1 && strstr(t1, "*"))); + int rhs_is_ptr = + (rhs->type_info->kind == TYPE_POINTER || + rhs->type_info->kind == TYPE_STRING || (t2 && strstr(t2, "*"))); + + if (lhs_is_ptr && rhs->type == NODE_EXPR_LITERAL && rhs->literal.int_val == 0) + { + skip_check = 1; + } + if (rhs_is_ptr && lhs->type == NODE_EXPR_LITERAL && lhs->literal.int_val == 0) + { + skip_check = 1; + } + } + if (!skip_check && !type_eq(lhs->type_info, rhs->type_info)) { char msg[256]; @@ -3493,8 +3533,10 @@ ASTNode *parse_expr_prec(ParserContext *ctx, Lexer *l, Precedence min_prec) if (strcmp(bin->binary.op, "+") == 0 || strcmp(bin->binary.op, "-") == 0) { int lhs_is_ptr = (lhs->type_info->kind == TYPE_POINTER || + lhs->type_info->kind == TYPE_STRING || (t1 && strstr(t1, "*") != NULL)); int rhs_is_ptr = (rhs->type_info->kind == TYPE_POINTER || + rhs->type_info->kind == TYPE_STRING || (t2 && strstr(t2, "*") != NULL)); int lhs_is_int = (lhs->type_info->kind == TYPE_INT || lhs->type_info->kind == TYPE_I32 || diff --git a/src/parser/parser_stmt.c b/src/parser/parser_stmt.c index 30c9e90..1b8637e 100644 --- a/src/parser/parser_stmt.c +++ b/src/parser/parser_stmt.c @@ -1168,10 +1168,6 @@ ASTNode *parse_var_decl(ParserContext *ctx, Lexer *l) type_obj = type_new(TYPE_STRUCT); type_obj->name = xstrdup(type); } - - // fprintf(stderr, "DEBUG PVarDecl: Var '%s' inferred type '%s' - // (init->type_info present: %d)\n", name, type, init && init->type_info ? - // 1 : 0); } } @@ -1553,21 +1549,18 @@ ASTNode *parse_for(ParserContext *ctx, Lexer *l) if (in_tok.type == TOK_IDENT && strncmp(in_tok.start, "in", 2) == 0) { - Token start_tok = lexer_next(l); - if (lexer_next(l).type == TOK_DOTDOT) + ASTNode *start_expr = parse_expression(ctx, l); + if (lexer_peek(l).type == TOK_DOTDOT) { - Token end_tok = lexer_next(l); + lexer_next(l); // consume .. + ASTNode *end_expr = parse_expression(ctx, l); ASTNode *n = ast_create(NODE_FOR_RANGE); n->for_range.var_name = xmalloc(var.len + 1); strncpy(n->for_range.var_name, var.start, var.len); n->for_range.var_name[var.len] = 0; - n->for_range.start = xmalloc(start_tok.len + 1); - strncpy(n->for_range.start, start_tok.start, start_tok.len); - n->for_range.start[start_tok.len] = 0; - n->for_range.end = xmalloc(end_tok.len + 1); - strncpy(n->for_range.end, end_tok.start, end_tok.len); - n->for_range.end[end_tok.len] = 0; + n->for_range.start = start_expr; + n->for_range.end = end_expr; if (lexer_peek(l).type == TOK_IDENT && strncmp(lexer_peek(l).start, "step", 4) == 0) { @@ -2118,6 +2111,10 @@ ASTNode *parse_statement(ParserContext *ctx, Lexer *l) else if (next_type == TOK_DOTDOT) { lexer_next(l); // consume .. + if (lexer_peek(l).type == TOK_SEMICOLON) + { + lexer_next(l); // consume optional ; + } } ASTNode *n = ast_create(NODE_RAW_STMT); diff --git a/src/parser/parser_utils.c b/src/parser/parser_utils.c index fcaab7f..ef66104 100644 --- a/src/parser/parser_utils.c +++ b/src/parser/parser_utils.c @@ -2148,9 +2148,10 @@ char *rewrite_expr_methods(ParserContext *ctx, char *raw) dest -= strlen(acc); - if (*src == '(') + Module *mod = find_module(ctx, acc); + if (mod && mod->is_c_header) { - dest += sprintf(dest, "%s_%s", acc, field); + dest += sprintf(dest, "%s", field); } else { diff --git a/src/zprep.h b/src/zprep.h index 299ec13..17e7e02 100644 --- a/src/zprep.h +++ b/src/zprep.h @@ -64,6 +64,8 @@ typedef enum TOK_DCOLON, TOK_TRAIT, TOK_IMPL, + TOK_AND, + TOK_OR, TOK_FOR, TOK_COMPTIME, TOK_ELLIPSIS, @@ -181,5 +183,10 @@ typedef struct } CompilerConfig; extern CompilerConfig g_config; +extern char g_link_flags[]; +extern char g_cflags[]; + +struct ParserContext; +void scan_build_directives(struct ParserContext *ctx, const char *src); #endif diff --git a/std/string.zc b/std/string.zc index 5c0327a..c3bf4ac 100644 --- a/std/string.zc +++ b/std/string.zc @@ -26,6 +26,10 @@ impl String { fn c_str(self) -> char* { return self.vec.data; } + + fn destroy(self) { + self.vec.free(); + } fn append(self, other: String) { self.vec.len = self.vec.len - 1; |
