Browse Source

`immutable` field prefix

Ginger Bill 8 years ago
parent
commit
563b1e2b28
7 changed files with 87 additions and 65 deletions
  1. 7 0
      code/demo.odin
  2. 2 0
      src/check_decl.c
  3. 3 0
      src/check_expr.c
  4. 3 6
      src/check_stmt.c
  5. 2 2
      src/entity.c
  6. 69 57
      src/parser.c
  7. 1 0
      src/tokenizer.c

+ 7 - 0
code/demo.odin

@@ -10,6 +10,13 @@
 #import "utf8.odin";
 #import "utf8.odin";
 
 
 main :: proc() {
 main :: proc() {
+	T :: struct { x, y: int }
+	foo :: proc(using immutable t: T) {
+		a0 := t.x;
+		a1 := x;
+		x = 123; // Error: Cannot assign to an immutable: `x`
+	}
+
 	// foo :: proc(x: ^i32) -> (int, int) {
 	// foo :: proc(x: ^i32) -> (int, int) {
 	// 	fmt.println("^int");
 	// 	fmt.println("^int");
 	// 	return 123, int(x^);
 	// 	return 123, int(x^);

+ 2 - 0
src/check_decl.c

@@ -538,6 +538,7 @@ void check_proc_body(Checker *c, Token token, DeclInfo *decl, Type *type, AstNod
 			if (!(e->flags & EntityFlag_Anonymous)) {
 			if (!(e->flags & EntityFlag_Anonymous)) {
 				continue;
 				continue;
 			}
 			}
+			bool is_immutable = e->Variable.is_immutable;
 			String name = e->token.string;
 			String name = e->token.string;
 			Type *t = base_type(type_deref(e->type));
 			Type *t = base_type(type_deref(e->type));
 			if (is_type_struct(t) || is_type_raw_union(t)) {
 			if (is_type_struct(t) || is_type_raw_union(t)) {
@@ -547,6 +548,7 @@ void check_proc_body(Checker *c, Token token, DeclInfo *decl, Type *type, AstNod
 					Entity *f = (*found)->elements.entries.e[i].value;
 					Entity *f = (*found)->elements.entries.e[i].value;
 					if (f->kind == Entity_Variable) {
 					if (f->kind == Entity_Variable) {
 						Entity *uvar = make_entity_using_variable(c->allocator, e, f->token, f->type);
 						Entity *uvar = make_entity_using_variable(c->allocator, e, f->token, f->type);
+						uvar->Variable.is_immutable = is_immutable;
 						Entity *prev = scope_insert_entity(c->context.scope, uvar);
 						Entity *prev = scope_insert_entity(c->context.scope, uvar);
 						if (prev != NULL) {
 						if (prev != NULL) {
 							error(e->token, "Namespace collision while `using` `%.*s` of: %.*s", LIT(name), LIT(prev->token.string));
 							error(e->token, "Namespace collision while `using` `%.*s` of: %.*s", LIT(name), LIT(prev->token.string));

+ 3 - 0
src/check_expr.c

@@ -782,6 +782,9 @@ Type *check_get_params(Checker *c, Scope *scope, AstNodeArray params, bool *is_v
 					if (p->flags&FieldFlag_no_alias) {
 					if (p->flags&FieldFlag_no_alias) {
 						param->flags |= EntityFlag_NoAlias;
 						param->flags |= EntityFlag_NoAlias;
 					}
 					}
+					if (p->flags&FieldFlag_immutable) {
+						param->Variable.is_immutable = true;
+					}
 					add_entity(c, scope, name, param);
 					add_entity(c, scope, name, param);
 					variables[variable_index++] = param;
 					variables[variable_index++] = param;
 				}
 				}

+ 3 - 6
src/check_stmt.c

@@ -270,13 +270,10 @@ Type *check_assignment_variable(Checker *c, Operand *op_a, AstNode *lhs) {
 		}
 		}
 
 
 		gbString str = expr_to_string(op_b.expr);
 		gbString str = expr_to_string(op_b.expr);
-		switch (op_b.mode) {
-		case Addressing_Value:
-			error_node(op_b.expr, "Cannot assign to `%s`", str);
-			break;
-		default:
+		if (e != NULL && e->kind == Entity_Variable && e->Variable.is_immutable) {
+			error_node(op_b.expr, "Cannot assign to an immutable: `%s`", str);
+		} else {
 			error_node(op_b.expr, "Cannot assign to `%s`", str);
 			error_node(op_b.expr, "Cannot assign to `%s`", str);
-			break;
 		}
 		}
 		gb_string_free(str);
 		gb_string_free(str);
 	} break;
 	} break;

+ 2 - 2
src/entity.c

@@ -34,8 +34,8 @@ typedef enum EntityFlag {
 	EntityFlag_Anonymous  = 1<<2,
 	EntityFlag_Anonymous  = 1<<2,
 	EntityFlag_Field      = 1<<3,
 	EntityFlag_Field      = 1<<3,
 	EntityFlag_Param      = 1<<4,
 	EntityFlag_Param      = 1<<4,
-	EntityFlag_Ellipsis   = 1<<5,
-	EntityFlag_VectorElem = 1<<6,
+	EntityFlag_VectorElem = 1<<5,
+	EntityFlag_Ellipsis   = 1<<6,
 	EntityFlag_NoAlias    = 1<<7,
 	EntityFlag_NoAlias    = 1<<7,
 } EntityFlag;
 } EntityFlag;
 
 

+ 69 - 57
src/parser.c

@@ -93,9 +93,12 @@ typedef enum StmtStateFlag {
 } StmtStateFlag;
 } StmtStateFlag;
 
 
 typedef enum FieldFlag {
 typedef enum FieldFlag {
-	FieldFlag_using    = 1<<0,
-	FieldFlag_no_alias = 1<<1,
-	FieldFlag_ellipsis = 1<<2,
+	FieldFlag_ellipsis  = 1<<0,
+	FieldFlag_using     = 1<<1,
+	FieldFlag_no_alias  = 1<<2,
+	FieldFlag_immutable = 1<<3,
+
+	FieldFlag_Signature = FieldFlag_ellipsis|FieldFlag_using|FieldFlag_no_alias|FieldFlag_immutable,
 } FieldListTag;
 } FieldListTag;
 
 
 AstNodeArray make_ast_node_array(AstFile *f) {
 AstNodeArray make_ast_node_array(AstFile *f) {
@@ -2234,26 +2237,6 @@ AstNode *parse_proc_type(AstFile *f, String *foreign_name_, String *link_name_)
 	return make_proc_type(f, proc_token, params, results, tags, cc);
 	return make_proc_type(f, proc_token, params, results, tags, cc);
 }
 }
 
 
-void parse_field_prefixes(AstFile *f, u32 flags, i32 *using_count, i32 *no_alias_count) {
-	while (f->curr_token.kind == Token_using ||
-	       f->curr_token.kind == Token_no_alias) {
-		if (allow_token(f, Token_using)) {
-			*using_count += 1;
-		}
-		if (allow_token(f, Token_no_alias)) {
-			*no_alias_count += 1;
-		}
-	}
-	if (*using_count > 1) {
-		syntax_error(f->curr_token, "Multiple `using` in this field list");
-		*using_count = 1;
-	}
-	if (*no_alias_count > 1) {
-		syntax_error(f->curr_token, "Multiple `no_alias` in this field list");
-		*no_alias_count = 1;
-	}
-}
-
 bool parse_expect_separator(AstFile *f, TokenKind separator, AstNode *param) {
 bool parse_expect_separator(AstFile *f, TokenKind separator, AstNode *param) {
 	if (separator == Token_Semicolon) {
 	if (separator == Token_Semicolon) {
 		expect_semicolon(f, param);
 		expect_semicolon(f, param);
@@ -2305,41 +2288,75 @@ AstNode *parse_var_type(AstFile *f, bool allow_ellipsis) {
 	return type;
 	return type;
 }
 }
 
 
-void check_field_prefixes(AstFile *f, AstNodeArray names, u32 flags, i32 *using_count, i32 *no_alias_count) {
-	if (names.count > 1 && *using_count > 0) {
-		syntax_error(f->curr_token, "Cannot apply `using` to more than one of the same type");
-		*using_count = 0;
-	}
 
 
-	if ((flags&FieldFlag_using) == 0 && *using_count > 0) {
-		syntax_error(f->curr_token, "`using` is not allowed within this field list");
-		*using_count = 0;
+u32 parse_field_prefixes(AstFile *f) {
+	i32 using_count = 0;
+	i32 no_alias_count = 0;
+	i32 immutable_count = 0;
+
+	while (f->curr_token.kind == Token_using ||
+	       f->curr_token.kind == Token_no_alias ||
+	       f->curr_token.kind == Token_immutable) {
+		if (allow_token(f, Token_using)) {
+			using_count += 1;
+		}
+		if (allow_token(f, Token_no_alias)) {
+			no_alias_count += 1;
+		}
+		if (allow_token(f, Token_immutable)) {
+			immutable_count += 1;
+		}
 	}
 	}
-	if ((flags&FieldFlag_no_alias) == 0 && *no_alias_count > 0) {
-		syntax_error(f->curr_token, "`no_alias` is not allowed within this field list");
-		*no_alias_count = 0;
+	if (using_count > 1) {
+		syntax_error(f->curr_token, "Multiple `using` in this field list");
+		using_count = 1;
+	}
+	if (no_alias_count > 1) {
+		syntax_error(f->curr_token, "Multiple `no_alias` in this field list");
+		no_alias_count = 1;
+	}
+	if (immutable_count > 1) {
+		syntax_error(f->curr_token, "Multiple `immutable` in this field list");
+		immutable_count = 1;
 	}
 	}
 
 
-}
 
 
-u32 field_prefixes_to_flags(i32 using_count, i32 no_alias_count) {
 	u32 field_flags = 0;
 	u32 field_flags = 0;
-	if (using_count    > 0) field_flags |= FieldFlag_using;
-	if (no_alias_count > 0) field_flags |= FieldFlag_no_alias;
+	if (using_count     > 0) field_flags |= FieldFlag_using;
+	if (no_alias_count  > 0) field_flags |= FieldFlag_no_alias;
+	if (immutable_count > 0) field_flags |= FieldFlag_immutable;
 	return field_flags;
 	return field_flags;
 }
 }
 
 
-AstNodeArray parse_field_list(AstFile *f, isize *name_count_, u32 flags,
+u32 check_field_prefixes(AstFile *f, AstNodeArray names, u32 allowed_flags, u32 set_flags) {
+	if (names.count > 1 && (set_flags&FieldFlag_using)) {
+		syntax_error(f->curr_token, "Cannot apply `using` to more than one of the same type");
+		set_flags &= ~FieldFlag_using;
+	}
+
+	if ((allowed_flags&FieldFlag_using) == 0 && (set_flags&FieldFlag_using)) {
+		syntax_error(f->curr_token, "`using` is not allowed within this field list");
+		set_flags &= ~FieldFlag_using;
+	}
+	if ((allowed_flags&FieldFlag_no_alias) == 0 && (set_flags&FieldFlag_no_alias)) {
+		syntax_error(f->curr_token, "`no_alias` is not allowed within this field list");
+		set_flags &= ~FieldFlag_no_alias;
+	}
+	if ((allowed_flags&FieldFlag_immutable) == 0 && (set_flags&FieldFlag_immutable)) {
+		syntax_error(f->curr_token, "`immutable` is not allowed within this field list");
+		set_flags &= ~FieldFlag_immutable;
+	}
+	return set_flags;
+}
+
+AstNodeArray parse_field_list(AstFile *f, isize *name_count_, u32 allowed_flags,
                               TokenKind separator, TokenKind follow) {
                               TokenKind separator, TokenKind follow) {
 	AstNodeArray params = make_ast_node_array(f);
 	AstNodeArray params = make_ast_node_array(f);
 	AstNodeArray list   = make_ast_node_array(f);
 	AstNodeArray list   = make_ast_node_array(f);
 	isize name_count    = 0;
 	isize name_count    = 0;
-	bool allow_ellipsis = flags&FieldFlag_ellipsis;
+	bool allow_ellipsis = allowed_flags&FieldFlag_ellipsis;
 
 
-	// TODO(bill): Allow for just a list of types
-	i32 using_count    = 0;
-	i32 no_alias_count = 0;
-	parse_field_prefixes(f, flags, &using_count, &no_alias_count);
+	u32 set_flags = parse_field_prefixes(f);
 	while (f->curr_token.kind != follow &&
 	while (f->curr_token.kind != follow &&
 	       f->curr_token.kind != Token_Colon &&
 	       f->curr_token.kind != Token_Colon &&
 	       f->curr_token.kind != Token_EOF) {
 	       f->curr_token.kind != Token_EOF) {
@@ -2356,35 +2373,30 @@ AstNodeArray parse_field_list(AstFile *f, isize *name_count_, u32 flags,
 		if (names.count == 0) {
 		if (names.count == 0) {
 			syntax_error(f->curr_token, "Empty field declaration");
 			syntax_error(f->curr_token, "Empty field declaration");
 		}
 		}
-		check_field_prefixes(f, names, flags, &using_count, &no_alias_count);
-
+		set_flags = check_field_prefixes(f, names, allowed_flags, set_flags);
 		name_count += names.count;
 		name_count += names.count;
 
 
 		expect_token_after(f, Token_Colon, "field list");
 		expect_token_after(f, Token_Colon, "field list");
 		AstNode *type = parse_var_type(f, allow_ellipsis);
 		AstNode *type = parse_var_type(f, allow_ellipsis);
-		AstNode *param = make_field(f, names, type, field_prefixes_to_flags(using_count, no_alias_count));
+		AstNode *param = make_field(f, names, type, set_flags);
 		array_add(&params, param);
 		array_add(&params, param);
 
 
 		parse_expect_separator(f, separator, type);
 		parse_expect_separator(f, separator, type);
 
 
 		while (f->curr_token.kind != follow &&
 		while (f->curr_token.kind != follow &&
 		       f->curr_token.kind != Token_EOF) {
 		       f->curr_token.kind != Token_EOF) {
-			i32 using_count    = 0;
-			i32 no_alias_count = 0;
-			parse_field_prefixes(f, flags, &using_count, &no_alias_count);
-
+			u32 set_flags = parse_field_prefixes(f);
 			AstNodeArray names = parse_ident_list(f);
 			AstNodeArray names = parse_ident_list(f);
 			if (names.count == 0) {
 			if (names.count == 0) {
 				syntax_error(f->curr_token, "Empty field declaration");
 				syntax_error(f->curr_token, "Empty field declaration");
 				break;
 				break;
 			}
 			}
-			check_field_prefixes(f, names, flags, &using_count, &no_alias_count);
+			set_flags = check_field_prefixes(f, names, allowed_flags, set_flags);
 			name_count += names.count;
 			name_count += names.count;
 
 
 			expect_token_after(f, Token_Colon, "field list");
 			expect_token_after(f, Token_Colon, "field list");
 			AstNode *type = parse_var_type(f, allow_ellipsis);
 			AstNode *type = parse_var_type(f, allow_ellipsis);
-
-			AstNode *param = make_field(f, names, type, field_prefixes_to_flags(using_count, no_alias_count));
+			AstNode *param = make_field(f, names, type, set_flags);
 			array_add(&params, param);
 			array_add(&params, param);
 
 
 			if (parse_expect_separator(f, separator, param)) {
 			if (parse_expect_separator(f, separator, param)) {
@@ -2396,7 +2408,7 @@ AstNodeArray parse_field_list(AstFile *f, isize *name_count_, u32 flags,
 		return params;
 		return params;
 	}
 	}
 
 
-	check_field_prefixes(f, list, flags, &using_count, &no_alias_count);
+	set_flags = check_field_prefixes(f, list, allowed_flags, set_flags);
 	for_array(i, list) {
 	for_array(i, list) {
 		AstNodeArray names = {0};
 		AstNodeArray names = {0};
 		AstNode *type = list.e[i];
 		AstNode *type = list.e[i];
@@ -2406,7 +2418,7 @@ AstNodeArray parse_field_list(AstFile *f, isize *name_count_, u32 flags,
 		token.pos = ast_node_token(type).pos;
 		token.pos = ast_node_token(type).pos;
 		names.e[0] = make_ident(f, token);
 		names.e[0] = make_ident(f, token);
 
 
-		AstNode *param = make_field(f, names, list.e[i], field_prefixes_to_flags(using_count, no_alias_count));
+		AstNode *param = make_field(f, names, list.e[i], set_flags);
 		array_add(&params, param);
 		array_add(&params, param);
 	}
 	}
 
 
@@ -2601,7 +2613,7 @@ void parse_proc_signature(AstFile *f,
                           AstNodeArray *params,
                           AstNodeArray *params,
                           AstNodeArray *results) {
                           AstNodeArray *results) {
 	expect_token(f, Token_OpenParen);
 	expect_token(f, Token_OpenParen);
-	*params = parse_field_list(f, NULL, FieldFlag_using|FieldFlag_no_alias|FieldFlag_ellipsis, Token_Comma, Token_CloseParen);
+	*params = parse_field_list(f, NULL, FieldFlag_Signature, Token_Comma, Token_CloseParen);
 	expect_token_after(f, Token_CloseParen, "parameter list");
 	expect_token_after(f, Token_CloseParen, "parameter list");
 	*results = parse_results(f);
 	*results = parse_results(f);
 }
 }

+ 1 - 0
src/tokenizer.c

@@ -106,6 +106,7 @@ TOKEN_KIND(Token__KeywordBegin, "_KeywordBegin"), \
 	TOKEN_KIND(Token_vector,         "vector"), \
 	TOKEN_KIND(Token_vector,         "vector"), \
 	TOKEN_KIND(Token_using,          "using"), \
 	TOKEN_KIND(Token_using,          "using"), \
 	TOKEN_KIND(Token_no_alias,       "no_alias"), \
 	TOKEN_KIND(Token_no_alias,       "no_alias"), \
+	TOKEN_KIND(Token_immutable,      "immutable"), \
 	TOKEN_KIND(Token_asm,            "asm"), \
 	TOKEN_KIND(Token_asm,            "asm"), \
 	TOKEN_KIND(Token_push_allocator, "push_allocator"), \
 	TOKEN_KIND(Token_push_allocator, "push_allocator"), \
 	TOKEN_KIND(Token_push_context,   "push_context"), \
 	TOKEN_KIND(Token_push_context,   "push_context"), \