summaryrefslogtreecommitdiff
path: root/src/parser/parser_expr.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/parser/parser_expr.c')
-rw-r--r--src/parser/parser_expr.c200
1 files changed, 196 insertions, 4 deletions
diff --git a/src/parser/parser_expr.c b/src/parser/parser_expr.c
index df2dcf6..7c53d96 100644
--- a/src/parser/parser_expr.c
+++ b/src/parser/parser_expr.c
@@ -1,3 +1,43 @@
+#include "../codegen/codegen.h"
+
+int check_opaque_alias_compat(ParserContext *ctx, Type *a, Type *b)
+{
+ if (!a || !b)
+ {
+ return 0;
+ }
+
+ int a_is_opaque = (a->kind == TYPE_ALIAS && a->alias.is_opaque_alias);
+ int b_is_opaque = (b->kind == TYPE_ALIAS && b->alias.is_opaque_alias);
+
+ if (!a_is_opaque && !b_is_opaque)
+ {
+ return 1;
+ }
+
+ if (a_is_opaque)
+ {
+ if (a->alias.alias_defined_in_file && g_current_filename &&
+ strcmp(a->alias.alias_defined_in_file, g_current_filename) == 0)
+ {
+ return check_opaque_alias_compat(ctx, a->inner, b);
+ }
+ return 0;
+ }
+
+ if (b_is_opaque)
+ {
+ if (b->alias.alias_defined_in_file && g_current_filename &&
+ strcmp(b->alias.alias_defined_in_file, g_current_filename) == 0)
+ {
+ return check_opaque_alias_compat(ctx, a, b->inner);
+ }
+ return 0;
+ }
+
+ return 0;
+}
+
#include "../zen/zen_facts.h"
#include "parser.h"
#include <ctype.h>
@@ -108,6 +148,8 @@ int is_type_copy(ParserContext *ctx, Type *t)
case TYPE_POINTER: // Pointers are Copy
case TYPE_FUNCTION:
case TYPE_ENUM: // Enums are integers
+ case TYPE_BITINT:
+ case TYPE_UBITINT:
return 1;
case TYPE_STRUCT:
@@ -116,6 +158,14 @@ int is_type_copy(ParserContext *ctx, Type *t)
{
return 1;
}
+
+ // If the struct is NOT defined (opaque/C type) and does NOT implement Drop,
+ // treat it as Copy (C behavior).
+ if (!find_struct_def(ctx, t->name) && !check_impl(ctx, "Drop", t->name))
+ {
+ return 1;
+ }
+
return 0;
case TYPE_ARRAY:
@@ -124,6 +174,13 @@ int is_type_copy(ParserContext *ctx, Type *t)
// but if it's a value assignment, C doesn't support it anyway unless wrapped in struct.
return 0;
+ case TYPE_ALIAS:
+ if (t->alias.is_opaque_alias)
+ {
+ return 1;
+ }
+ return is_type_copy(ctx, t->inner);
+
default:
return 1;
}
@@ -935,6 +992,17 @@ static ASTNode *create_fstring_block(ParserContext *ctx, const char *content)
free(txt);
}
+ // Handle escape {{
+ if (brace[1] == '{')
+ {
+ ASTNode *cat = ast_create(NODE_RAW_STMT);
+ cat->raw_stmt.content = xstrdup("strcat(_b, \"{\");");
+ tail->next = cat;
+ tail = cat;
+ cur = brace + 2;
+ continue;
+ }
+
char *end_brace = strchr(brace, '}');
if (!end_brace)
{
@@ -1072,6 +1140,7 @@ static ASTNode *create_fstring_block(ParserContext *ctx, const char *content)
static ASTNode *parse_int_literal(Token t)
{
ASTNode *node = ast_create(NODE_EXPR_LITERAL);
+ node->token = t;
node->literal.type_kind = LITERAL_INT;
node->type_info = type_new(TYPE_INT);
char *s = token_strdup(t);
@@ -1093,6 +1162,7 @@ static ASTNode *parse_int_literal(Token t)
static ASTNode *parse_float_literal(Token t)
{
ASTNode *node = ast_create(NODE_EXPR_LITERAL);
+ node->token = t;
node->literal.type_kind = LITERAL_FLOAT;
node->literal.float_val = atof(t.start);
node->type_info = type_new(TYPE_F64);
@@ -1100,9 +1170,32 @@ static ASTNode *parse_float_literal(Token t)
}
// Parse string literal
-static ASTNode *parse_string_literal(Token t)
+static ASTNode *parse_string_literal(ParserContext *ctx, Token t)
{
+ // Check for implicit interpolation
+ int has_interpolation = 0;
+ for (int i = 1; i < t.len - 1; i++)
+ {
+ if (t.start[i] == '{')
+ {
+ has_interpolation = 1;
+ break;
+ }
+ }
+
+ if (has_interpolation)
+ {
+
+ char *inner = xmalloc(t.len);
+ strncpy(inner, t.start + 1, t.len - 2);
+ inner[t.len - 2] = 0;
+ ASTNode *node = create_fstring_block(ctx, inner);
+ free(inner);
+ return node;
+ }
+
ASTNode *node = ast_create(NODE_EXPR_LITERAL);
+ node->token = t;
node->literal.type_kind = LITERAL_STRING;
node->literal.string_val = xmalloc(t.len);
strncpy(node->literal.string_val, t.start + 1, t.len - 2);
@@ -1126,6 +1219,7 @@ static ASTNode *parse_fstring_literal(ParserContext *ctx, Token t)
static ASTNode *parse_char_literal(Token t)
{
ASTNode *node = ast_create(NODE_EXPR_LITERAL);
+ node->token = t;
node->literal.type_kind = LITERAL_CHAR;
node->literal.string_val = token_strdup(t);
node->type_info = type_new(TYPE_I8);
@@ -1276,7 +1370,7 @@ ASTNode *parse_primary(ParserContext *ctx, Lexer *l)
}
else if (t.type == TOK_STRING)
{
- node = parse_string_literal(t);
+ node = parse_string_literal(ctx, t);
}
else if (t.type == TOK_FSTRING)
{
@@ -2018,6 +2112,20 @@ ASTNode *parse_primary(ParserContext *ctx, Lexer *l)
sprintf(prefixed, "%s_%s", ctx->current_module_prefix, acc);
struct_name = prefixed;
}
+
+ // Opaque Struct Check
+ ASTNode *def = find_struct_def(ctx, struct_name);
+ if (def && def->type == NODE_STRUCT && def->strct.is_opaque)
+ {
+ if (!def->strct.defined_in_file ||
+ (g_current_filename &&
+ strcmp(def->strct.defined_in_file, g_current_filename) != 0))
+ {
+ zpanic_at(lexer_peek(l),
+ "Cannot initialize opaque struct '%s' outside its module",
+ struct_name);
+ }
+ }
lexer_next(l);
node = ast_create(NODE_EXPR_STRUCT_INIT);
node->struct_init.struct_name = struct_name;
@@ -3592,7 +3700,7 @@ ASTNode *parse_expr_prec(ParserContext *ctx, Lexer *l, Precedence min_prec)
}
else if (t->kind == TYPE_STRING)
{
- strcat(fmt, "%s");
+ strcat(fmt, "%ms");
}
else if (t->kind == TYPE_CHAR || t->kind == TYPE_I8 || t->kind == TYPE_U8 ||
t->kind == TYPE_BYTE)
@@ -4012,6 +4120,30 @@ ASTNode *parse_expr_prec(ParserContext *ctx, Lexer *l, Precedence min_prec)
node->member.field = token_strdup(field);
node->member.is_pointer_access = 1;
+ // Opaque Check
+ int is_ptr_dummy = 0;
+ char *alloc_name = NULL;
+ char *sname =
+ resolve_struct_name_from_type(ctx, lhs->type_info, &is_ptr_dummy, &alloc_name);
+ if (sname)
+ {
+ ASTNode *def = find_struct_def(ctx, sname);
+ if (def && def->type == NODE_STRUCT && def->strct.is_opaque)
+ {
+ if (!def->strct.defined_in_file ||
+ (g_current_filename &&
+ strcmp(def->strct.defined_in_file, g_current_filename) != 0))
+ {
+ zpanic_at(field, "Cannot access private field '%s' of opaque struct '%s'",
+ node->member.field, sname);
+ }
+ }
+ if (alloc_name)
+ {
+ free(alloc_name);
+ }
+ }
+
node->type_info = get_field_type(ctx, lhs->type_info, node->member.field);
if (node->type_info)
{
@@ -4041,6 +4173,30 @@ ASTNode *parse_expr_prec(ParserContext *ctx, Lexer *l, Precedence min_prec)
node->member.field = token_strdup(field);
node->member.is_pointer_access = 2;
+ // Opaque Check
+ int is_ptr_dummy = 0;
+ char *alloc_name = NULL;
+ char *sname =
+ resolve_struct_name_from_type(ctx, lhs->type_info, &is_ptr_dummy, &alloc_name);
+ if (sname)
+ {
+ ASTNode *def = find_struct_def(ctx, sname);
+ if (def && def->type == NODE_STRUCT && def->strct.is_opaque)
+ {
+ if (!def->strct.defined_in_file ||
+ (g_current_filename &&
+ strcmp(def->strct.defined_in_file, g_current_filename) != 0))
+ {
+ zpanic_at(field, "Cannot access private field '%s' of opaque struct '%s'",
+ node->member.field, sname);
+ }
+ }
+ if (alloc_name)
+ {
+ free(alloc_name);
+ }
+ }
+
node->type_info = get_field_type(ctx, lhs->type_info, node->member.field);
if (node->type_info)
{
@@ -4620,6 +4776,30 @@ ASTNode *parse_expr_prec(ParserContext *ctx, Lexer *l, Precedence min_prec)
node->member.field = token_strdup(field);
node->member.is_pointer_access = 0;
+ // Opaque Check
+ int is_ptr_dummy = 0;
+ char *alloc_name = NULL;
+ char *sname =
+ resolve_struct_name_from_type(ctx, lhs->type_info, &is_ptr_dummy, &alloc_name);
+ if (sname)
+ {
+ ASTNode *def = find_struct_def(ctx, sname);
+ if (def && def->type == NODE_STRUCT && def->strct.is_opaque)
+ {
+ if (!def->strct.defined_in_file ||
+ (g_current_filename &&
+ strcmp(def->strct.defined_in_file, g_current_filename) != 0))
+ {
+ zpanic_at(field, "Cannot access private field '%s' of opaque struct '%s'",
+ node->member.field, sname);
+ }
+ }
+ if (alloc_name)
+ {
+ free(alloc_name);
+ }
+ }
+
node->member.field = token_strdup(field);
node->member.is_pointer_access = 0;
@@ -5241,6 +5421,7 @@ ASTNode *parse_expr_prec(ParserContext *ctx, Lexer *l, Precedence min_prec)
// This gives a warning as "unused" but it's needed for the rewrite.
char *r_name =
resolve_struct_name_from_type(ctx, rhs->type_info, &is_rhs_ptr, &r_alloc);
+ (void)r_name;
if (r_alloc)
{
free(r_alloc);
@@ -5463,7 +5644,17 @@ ASTNode *parse_expr_prec(ParserContext *ctx, Lexer *l, Precedence min_prec)
char *t1 = type_to_string(lhs->type_info);
char *t2 = type_to_string(rhs->type_info);
// Skip type check if either operand is void* (escape hatch type)
+ // or if either operand is a generic type parameter (T, K, V, etc.)
int skip_check = (strcmp(t1, "void*") == 0 || strcmp(t2, "void*") == 0);
+ if (lhs->type_info->kind == TYPE_GENERIC || rhs->type_info->kind == TYPE_GENERIC)
+ {
+ skip_check = 1;
+ }
+ // Also check if type name is a single uppercase letter (common generic param)
+ if ((strlen(t1) == 1 && isupper(t1[0])) || (strlen(t2) == 1 && isupper(t2[0])))
+ {
+ skip_check = 1;
+ }
// Allow comparing pointers/strings with integer literal 0 (NULL)
if (!skip_check)
@@ -5507,7 +5698,8 @@ ASTNode *parse_expr_prec(ParserContext *ctx, Lexer *l, Precedence min_prec)
}
else
{
- if (type_eq(lhs->type_info, rhs->type_info))
+ if (type_eq(lhs->type_info, rhs->type_info) ||
+ check_opaque_alias_compat(ctx, lhs->type_info, rhs->type_info))
{
bin->type_info = lhs->type_info;
}