authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2016-01-07 05:29:11-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2016-01-07 05:29:11-07:00
log9aea99a999997e223307d8559e0ff9fa613839a3
tree241d9f8fc2b8225a4941bbf8b748170eb49411e7
parentea69d6ecda8412cf47b85853c8645d453c826427

implement array slicing syntax

closes #52

7 files changed, 199 insertions(+), 18 deletions(-)

doc/langref.md+3-1
......@@ -148,7 +148,7 @@ CastExpression : CastExpression token(as) Type | PrefixOpExpression
148148
149149PrefixOpExpression : PrefixOp PrefixOpExpression | SuffixOpExpression
150150
151SuffixOpExpression : PrimaryExpression option(FnCallExpression | ArrayAccessExpression | FieldAccessExpression)
151SuffixOpExpression : PrimaryExpression option(FnCallExpression | ArrayAccessExpression | FieldAccessExpression | SliceExpression)
152152
153153FieldAccessExpression : token(Dot) token(Symbol)
154154
......@@ -156,6 +156,8 @@ FnCallExpression : token(LParen) list(Expression, token(Comma)) token(RParen)
156156
157157ArrayAccessExpression : token(LBracket) Expression token(RBracket)
158158
159SliceExpression : token(LBracket) Expression token(Ellipsis) option(Expression) token(RBracket) option(token(Const))
160
159161PrefixOp : token(Not) | token(Dash) | token(Tilde) | token(Star) | (token(Ampersand) option(token(Const)))
160162
161163PrimaryExpression : token(Number) | token(String) | token(CharLiteral) | KeywordLiteral | GroupedExpression | Goto | token(Break) | token(Continue) | BlockExpression | token(Symbol) | StructValueExpression | CompilerFnType
src/analyze.cpp+54-1
......@@ -23,6 +23,8 @@ static AstNode *first_executing_node(AstNode *node) {
2323 return first_executing_node(node->data.bin_op_expr.op1);
2424 case NodeTypeArrayAccessExpr:
2525 return first_executing_node(node->data.array_access_expr.array_ref_expr);
26 case NodeTypeSliceExpr:
27 return first_executing_node(node->data.slice_expr.array_ref_expr);
2628 case NodeTypeFieldAccessExpr:
2729 return first_executing_node(node->data.field_access_expr.struct_expr);
2830 case NodeTypeCastExpr:
......@@ -875,6 +877,7 @@ static void preview_function_declarations(CodeGen *g, ImportTableEntry *import,
875877 case NodeTypeBinOpExpr:
876878 case NodeTypeFnCallExpr:
877879 case NodeTypeArrayAccessExpr:
880 case NodeTypeSliceExpr:
878881 case NodeTypeNumberLiteral:
879882 case NodeTypeStringLiteral:
880883 case NodeTypeCharLiteral:
......@@ -950,6 +953,7 @@ static void preview_types(CodeGen *g, ImportTableEntry *import, AstNode *node) {
950953 case NodeTypeBinOpExpr:
951954 case NodeTypeFnCallExpr:
952955 case NodeTypeArrayAccessExpr:
956 case NodeTypeSliceExpr:
953957 case NodeTypeNumberLiteral:
954958 case NodeTypeStringLiteral:
955959 case NodeTypeCharLiteral:
......@@ -1349,6 +1353,50 @@ static TypeTableEntry *analyze_field_access_expr(CodeGen *g, ImportTableEntry *i
13491353 return return_type;
13501354}
13511355
1356static TypeTableEntry *analyze_slice_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context,
1357 AstNode *node)
1358{
1359 TypeTableEntry *array_type = analyze_expression(g, import, context, nullptr,
1360 node->data.slice_expr.array_ref_expr);
1361
1362 TypeTableEntry *return_type;
1363
1364 if (array_type->id == TypeTableEntryIdInvalid) {
1365 return_type = g->builtin_types.entry_invalid;
1366 } else if (array_type->id == TypeTableEntryIdArray) {
1367 return_type = get_unknown_size_array_type(g, import, array_type->data.array.child_type,
1368 node->data.slice_expr.is_const);
1369 } else if (array_type->id == TypeTableEntryIdPointer) {
1370 return_type = get_unknown_size_array_type(g, import, array_type->data.pointer.child_type,
1371 node->data.slice_expr.is_const);
1372 } else if (array_type->id == TypeTableEntryIdStruct &&
1373 array_type->data.structure.is_unknown_size_array)
1374 {
1375 return_type = get_unknown_size_array_type(g, import,
1376 array_type->data.structure.fields[0].type_entry,
1377 node->data.slice_expr.is_const);
1378 } else {
1379 add_node_error(g, node,
1380 buf_sprintf("slice of non-array type '%s'", buf_ptr(&array_type->name)));
1381 return_type = g->builtin_types.entry_invalid;
1382 }
1383
1384 if (return_type->id != TypeTableEntryIdInvalid) {
1385 assert(node->codegen_node);
1386 node->codegen_node->data.struct_val_expr_node.type_entry = return_type;
1387 node->codegen_node->data.struct_val_expr_node.source_node = node;
1388 context->struct_val_expr_alloca_list.append(&node->codegen_node->data.struct_val_expr_node);
1389 }
1390
1391 analyze_expression(g, import, context, g->builtin_types.entry_usize, node->data.slice_expr.start);
1392
1393 if (node->data.slice_expr.end) {
1394 analyze_expression(g, import, context, g->builtin_types.entry_usize, node->data.slice_expr.end);
1395 }
1396
1397 return return_type;
1398}
1399
13521400static TypeTableEntry *analyze_array_access_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context,
13531401 AstNode *node)
13541402{
......@@ -1363,7 +1411,8 @@ static TypeTableEntry *analyze_array_access_expr(CodeGen *g, ImportTableEntry *i
13631411 return_type = array_type->data.pointer.child_type;
13641412 } else {
13651413 if (array_type->id != TypeTableEntryIdInvalid) {
1366 add_node_error(g, node, buf_sprintf("array access of non-array"));
1414 add_node_error(g, node,
1415 buf_sprintf("array access of non-array type '%s'", buf_ptr(&array_type->name)));
13671416 }
13681417 return_type = g->builtin_types.entry_invalid;
13691418 }
......@@ -2197,6 +2246,9 @@ static TypeTableEntry * analyze_expression(CodeGen *g, ImportTableEntry *import,
21972246 // for reading array access; assignment handled elsewhere
21982247 return_type = analyze_array_access_expr(g, import, context, node);
21992248 break;
2249 case NodeTypeSliceExpr:
2250 return_type = analyze_slice_expr(g, import, context, node);
2251 break;
22002252 case NodeTypeFieldAccessExpr:
22012253 return_type = analyze_field_access_expr(g, import, context, node);
22022254 break;
......@@ -2541,6 +2593,7 @@ static void analyze_top_level_declaration(CodeGen *g, ImportTableEntry *import,
25412593 case NodeTypeBinOpExpr:
25422594 case NodeTypeFnCallExpr:
25432595 case NodeTypeArrayAccessExpr:
2596 case NodeTypeSliceExpr:
25442597 case NodeTypeNumberLiteral:
25452598 case NodeTypeStringLiteral:
25462599 case NodeTypeCharLiteral:
src/analyze.hpp+2
......@@ -355,9 +355,11 @@ struct CodeGenNode {
355355 StructDeclNode struct_decl_node; // for NodeTypeStructDecl
356356 FieldAccessNode field_access_node; // for NodeTypeFieldAccessExpr
357357 CastNode cast_node; // for NodeTypeCastExpr
358 // note: I've been using this field on some non-number literal nodes too.
358359 NumberLiteralNode num_lit_node; // for NodeTypeNumberLiteral
359360 VarDeclNode var_decl_node; // for NodeTypeVariableDeclaration
360361 StructValFieldNode struct_val_field_node; // for NodeTypeStructValueField
362 // note: I've been using this field on some non-struct val expressions too.
361363 StructValExprNode struct_val_expr_node; // for NodeTypeStructValueExpr
362364 IfVarNode if_var_node; // for NodeTypeStructValueExpr
363365 ParamDeclNode param_decl_node; // for NodeTypeParamDecl
src/codegen.cpp+60-8
......@@ -215,26 +215,34 @@ static LLVMValueRef gen_fn_call_expr(CodeGen *g, AstNode *node) {
215215 }
216216}
217217
218static LLVMValueRef gen_array_ptr(CodeGen *g, AstNode *node) {
219 assert(node->type == NodeTypeArrayAccessExpr);
220
221 AstNode *array_expr_node = node->data.array_access_expr.array_ref_expr;
222 TypeTableEntry *type_entry = get_expr_type(array_expr_node);
218static LLVMValueRef gen_array_base_ptr(CodeGen *g, AstNode *node) {
219 TypeTableEntry *type_entry = get_expr_type(node);
223220
224221 LLVMValueRef array_ptr;
225 if (array_expr_node->type == NodeTypeFieldAccessExpr) {
226 array_ptr = gen_field_access_expr(g, array_expr_node, true);
222 if (node->type == NodeTypeFieldAccessExpr) {
223 array_ptr = gen_field_access_expr(g, node, true);
227224 if (type_entry->id == TypeTableEntryIdPointer) {
228225 // we have a double pointer so we must dereference it once
229226 add_debug_source_node(g, node);
230227 array_ptr = LLVMBuildLoad(g->builder, array_ptr, "");
231228 }
232229 } else {
233 array_ptr = gen_expr(g, array_expr_node);
230 array_ptr = gen_expr(g, node);
234231 }
235232
236233 assert(LLVMGetTypeKind(LLVMTypeOf(array_ptr)) == LLVMPointerTypeKind);
237234
235 return array_ptr;
236}
237
238static LLVMValueRef gen_array_ptr(CodeGen *g, AstNode *node) {
239 assert(node->type == NodeTypeArrayAccessExpr);
240
241 AstNode *array_expr_node = node->data.array_access_expr.array_ref_expr;
242 TypeTableEntry *type_entry = get_expr_type(array_expr_node);
243
244 LLVMValueRef array_ptr = gen_array_base_ptr(g, array_expr_node);
245
238246 LLVMValueRef subscript_value = gen_expr(g, node->data.array_access_expr.subscript);
239247 assert(subscript_value);
240248
......@@ -299,6 +307,48 @@ static LLVMValueRef gen_field_ptr(CodeGen *g, AstNode *node, TypeTableEntry **ou
299307 return LLVMBuildStructGEP(g->builder, struct_ptr, codegen_field_access->field_index, "");
300308}
301309
310static LLVMValueRef gen_slice_expr(CodeGen *g, AstNode *node) {
311 assert(node->type == NodeTypeSliceExpr);
312
313 AstNode *array_ref_node = node->data.slice_expr.array_ref_expr;
314 TypeTableEntry *array_type = get_expr_type(array_ref_node);
315
316 LLVMValueRef tmp_struct_ptr = node->codegen_node->data.struct_val_expr_node.ptr;
317
318 if (array_type->id == TypeTableEntryIdArray) {
319 LLVMValueRef array_ptr = gen_array_base_ptr(g, array_ref_node);
320 LLVMValueRef start_val = gen_expr(g, node->data.slice_expr.start);
321 LLVMValueRef end_val;
322 if (node->data.slice_expr.end) {
323 end_val = gen_expr(g, node->data.slice_expr.end);
324 } else {
325 end_val = LLVMConstInt(g->builtin_types.entry_usize->type_ref, array_type->data.array.len, false);
326 }
327
328 add_debug_source_node(g, node);
329 LLVMValueRef ptr_field_ptr = LLVMBuildStructGEP(g->builder, tmp_struct_ptr, 0, "");
330 LLVMValueRef indices[] = {
331 LLVMConstNull(g->builtin_types.entry_usize->type_ref),
332 start_val,
333 };
334 LLVMValueRef slice_start_ptr = LLVMBuildInBoundsGEP(g->builder, array_ptr, indices, 2, "");
335 LLVMBuildStore(g->builder, slice_start_ptr, ptr_field_ptr);
336
337 LLVMValueRef len_field_ptr = LLVMBuildStructGEP(g->builder, tmp_struct_ptr, 1, "");
338 LLVMValueRef len_value = LLVMBuildSub(g->builder, end_val, start_val, "");
339 LLVMBuildStore(g->builder, len_value, len_field_ptr);
340
341 return tmp_struct_ptr;
342 } else if (array_type->id == TypeTableEntryIdPointer) {
343 zig_panic("TODO gen_slice_expr pointer");
344 } else if (array_type->id == TypeTableEntryIdStruct) {
345 assert(array_type->data.structure.is_unknown_size_array);
346 zig_panic("TODO gen_slice_expr unknown size array");
347 } else {
348 zig_unreachable();
349 }
350}
351
302352static LLVMValueRef gen_array_access_expr(CodeGen *g, AstNode *node, bool is_lvalue) {
303353 assert(node->type == NodeTypeArrayAccessExpr);
304354
......@@ -1443,6 +1493,8 @@ static LLVMValueRef gen_expr_no_cast(CodeGen *g, AstNode *node) {
14431493 return gen_fn_call_expr(g, node);
14441494 case NodeTypeArrayAccessExpr:
14451495 return gen_array_access_expr(g, node, false);
1496 case NodeTypeSliceExpr:
1497 return gen_slice_expr(g, node);
14461498 case NodeTypeFieldAccessExpr:
14471499 return gen_field_access_expr(g, node, false);
14481500 case NodeTypeUnreachable:
src/parser.cpp+42-8
......@@ -90,6 +90,8 @@ const char *node_type_str(NodeType node_type) {
9090 return "FnCallExpr";
9191 case NodeTypeArrayAccessExpr:
9292 return "ArrayAccessExpr";
93 case NodeTypeSliceExpr:
94 return "SliceExpr";
9395 case NodeTypeExternBlock:
9496 return "ExternBlock";
9597 case NodeTypeDirective:
......@@ -298,6 +300,14 @@ void ast_print(AstNode *node, int indent) {
298300 ast_print(node->data.array_access_expr.array_ref_expr, indent + 2);
299301 ast_print(node->data.array_access_expr.subscript, indent + 2);
300302 break;
303 case NodeTypeSliceExpr:
304 fprintf(stderr, "%s\n", node_type_str(node->type));
305 ast_print(node->data.slice_expr.array_ref_expr, indent + 2);
306 ast_print(node->data.slice_expr.start, indent + 2);
307 if (node->data.slice_expr.end) {
308 ast_print(node->data.slice_expr.end, indent + 2);
309 }
310 break;
301311 case NodeTypeDirective:
302312 fprintf(stderr, "%s\n", node_type_str(node->type));
303313 break;
......@@ -1381,9 +1391,10 @@ static AstNode *ast_parse_primary_expr(ParseContext *pc, int *token_index, bool
13811391}
13821392
13831393/*
1384SuffixOpExpression : PrimaryExpression option(FnCallExpression | ArrayAccessExpression | FieldAccessExpression)
1394SuffixOpExpression : PrimaryExpression option(FnCallExpression | ArrayAccessExpression | FieldAccessExpression | SliceExpression)
13851395FnCallExpression : token(LParen) list(Expression, token(Comma)) token(RParen)
13861396ArrayAccessExpression : token(LBracket) Expression token(RBracket)
1397SliceExpression : token(LBracket) Expression token(Ellipsis) option(Expression) token(RBracket) option(token(Const))
13871398FieldAccessExpression : token(Dot) token(Symbol)
13881399*/
13891400static AstNode *ast_parse_suffix_op_expr(ParseContext *pc, int *token_index, bool mandatory) {
......@@ -1405,15 +1416,38 @@ static AstNode *ast_parse_suffix_op_expr(ParseContext *pc, int *token_index, boo
14051416 } else if (token->id == TokenIdLBracket) {
14061417 *token_index += 1;
14071418
1408 AstNode *node = ast_create_node(pc, NodeTypeArrayAccessExpr, token);
1409 node->data.array_access_expr.array_ref_expr = primary_expr;
1410 node->data.array_access_expr.subscript = ast_parse_expression(pc, token_index, true);
1419 AstNode *expr_node = ast_parse_expression(pc, token_index, true);
14111420
1412 Token *r_bracket = &pc->tokens->at(*token_index);
1413 *token_index += 1;
1414 ast_expect_token(pc, r_bracket, TokenIdRBracket);
1421 Token *ellipsis_or_r_bracket = &pc->tokens->at(*token_index);
14151422
1416 primary_expr = node;
1423 if (ellipsis_or_r_bracket->id == TokenIdEllipsis) {
1424 *token_index += 1;
1425
1426 AstNode *node = ast_create_node(pc, NodeTypeSliceExpr, token);
1427 node->data.slice_expr.array_ref_expr = primary_expr;
1428 node->data.slice_expr.start = expr_node;
1429 node->data.slice_expr.end = ast_parse_expression(pc, token_index, false);
1430
1431 ast_eat_token(pc, token_index, TokenIdRBracket);
1432
1433 Token *const_tok = &pc->tokens->at(*token_index);
1434 if (const_tok->id == TokenIdKeywordConst) {
1435 *token_index += 1;
1436 node->data.slice_expr.is_const = true;
1437 }
1438
1439 primary_expr = node;
1440 } else if (ellipsis_or_r_bracket->id == TokenIdRBracket) {
1441 *token_index += 1;
1442
1443 AstNode *node = ast_create_node(pc, NodeTypeArrayAccessExpr, token);
1444 node->data.array_access_expr.array_ref_expr = primary_expr;
1445 node->data.array_access_expr.subscript = expr_node;
1446
1447 primary_expr = node;
1448 } else {
1449 ast_invalid_token_error(pc, token);
1450 }
14171451 } else if (token->id == TokenIdDot) {
14181452 *token_index += 1;
14191453
src/parser.hpp+9
......@@ -41,6 +41,7 @@ enum NodeType {
4141 NodeTypePrefixOpExpr,
4242 NodeTypeFnCallExpr,
4343 NodeTypeArrayAccessExpr,
44 NodeTypeSliceExpr,
4445 NodeTypeFieldAccessExpr,
4546 NodeTypeUse,
4647 NodeTypeVoid,
......@@ -181,6 +182,13 @@ struct AstNodeArrayAccessExpr {
181182 AstNode *subscript;
182183};
183184
185struct AstNodeSliceExpr {
186 AstNode *array_ref_expr;
187 AstNode *start;
188 AstNode *end;
189 bool is_const;
190};
191
184192struct AstNodeFieldAccessExpr {
185193 AstNode *struct_expr;
186194 Buf field_name;
......@@ -378,6 +386,7 @@ struct AstNode {
378386 AstNodePrefixOpExpr prefix_op_expr;
379387 AstNodeFnCallExpr fn_call_expr;
380388 AstNodeArrayAccessExpr array_access_expr;
389 AstNodeSliceExpr slice_expr;
381390 AstNodeUse use;
382391 AstNodeIfBoolExpr if_bool_expr;
383392 AstNodeIfVarExpr if_var_expr;
test/run_tests.cpp+29
......@@ -907,6 +907,35 @@ pub fn main(argc: isize, argv: &&u8, env: &&u8) -> i32 {
907907 "min i16: -32768\n"
908908 "min i32: -2147483648\n"
909909 "min i64: -9223372036854775808\n");
910
911
912 add_simple_case("slicing", R"SOURCE(
913use "std.zig";
914pub fn main(argc: isize, argv: &&u8, env: &&u8) -> i32 {
915 var array : [20]i32;
916
917 array[5] = 1234;
918
919 var slice = array[5...10];
920
921 if (slice.len != 5) {
922 print_str("BAD\n");
923 }
924
925 if (slice.ptr[0] != 1234) {
926 print_str("BAD\n");
927 }
928
929 var slice_rest = array[10...];
930 if (slice_rest.len != 10) {
931 print_str("BAD\n");
932 }
933
934 print_str("OK\n");
935 return 0;
936}
937 )SOURCE", "OK\n");
938
910939}
911940
912941////////////////////////////////////////////////////////////////////////////////////