| author | |
| committer | |
| log | 9aea99a999997e223307d8559e0ff9fa613839a3 |
| tree | 241d9f8fc2b8225a4941bbf8b748170eb49411e7 |
| parent | ea69d6ecda8412cf47b85853c8645d453c826427 |
closes #527 files changed, 199 insertions(+), 18 deletions(-)
doc/langref.md+3-1| ... | ... | @@ -148,7 +148,7 @@ CastExpression : CastExpression token(as) Type | PrefixOpExpression |
| 148 | 148 | |
| 149 | 149 | PrefixOpExpression : PrefixOp PrefixOpExpression | SuffixOpExpression |
| 150 | 150 | |
| 151 | SuffixOpExpression : PrimaryExpression option(FnCallExpression | ArrayAccessExpression | FieldAccessExpression) | |
| 151 | SuffixOpExpression : PrimaryExpression option(FnCallExpression | ArrayAccessExpression | FieldAccessExpression | SliceExpression) | |
| 152 | 152 | |
| 153 | 153 | FieldAccessExpression : token(Dot) token(Symbol) |
| 154 | 154 | |
| ... | ... | @@ -156,6 +156,8 @@ FnCallExpression : token(LParen) list(Expression, token(Comma)) token(RParen) |
| 156 | 156 | |
| 157 | 157 | ArrayAccessExpression : token(LBracket) Expression token(RBracket) |
| 158 | 158 | |
| 159 | SliceExpression : token(LBracket) Expression token(Ellipsis) option(Expression) token(RBracket) option(token(Const)) | |
| 160 | ||
| 159 | 161 | PrefixOp : token(Not) | token(Dash) | token(Tilde) | token(Star) | (token(Ampersand) option(token(Const))) |
| 160 | 162 | |
| 161 | 163 | PrimaryExpression : 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) { |
| 23 | 23 | return first_executing_node(node->data.bin_op_expr.op1); |
| 24 | 24 | case NodeTypeArrayAccessExpr: |
| 25 | 25 | 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); | |
| 26 | 28 | case NodeTypeFieldAccessExpr: |
| 27 | 29 | return first_executing_node(node->data.field_access_expr.struct_expr); |
| 28 | 30 | case NodeTypeCastExpr: |
| ... | ... | @@ -875,6 +877,7 @@ static void preview_function_declarations(CodeGen *g, ImportTableEntry *import, |
| 875 | 877 | case NodeTypeBinOpExpr: |
| 876 | 878 | case NodeTypeFnCallExpr: |
| 877 | 879 | case NodeTypeArrayAccessExpr: |
| 880 | case NodeTypeSliceExpr: | |
| 878 | 881 | case NodeTypeNumberLiteral: |
| 879 | 882 | case NodeTypeStringLiteral: |
| 880 | 883 | case NodeTypeCharLiteral: |
| ... | ... | @@ -950,6 +953,7 @@ static void preview_types(CodeGen *g, ImportTableEntry *import, AstNode *node) { |
| 950 | 953 | case NodeTypeBinOpExpr: |
| 951 | 954 | case NodeTypeFnCallExpr: |
| 952 | 955 | case NodeTypeArrayAccessExpr: |
| 956 | case NodeTypeSliceExpr: | |
| 953 | 957 | case NodeTypeNumberLiteral: |
| 954 | 958 | case NodeTypeStringLiteral: |
| 955 | 959 | case NodeTypeCharLiteral: |
| ... | ... | @@ -1349,6 +1353,50 @@ static TypeTableEntry *analyze_field_access_expr(CodeGen *g, ImportTableEntry *i |
| 1349 | 1353 | return return_type; |
| 1350 | 1354 | } |
| 1351 | 1355 | |
| 1356 | static 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 | ||
| 1352 | 1400 | static TypeTableEntry *analyze_array_access_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context, |
| 1353 | 1401 | AstNode *node) |
| 1354 | 1402 | { |
| ... | ... | @@ -1363,7 +1411,8 @@ static TypeTableEntry *analyze_array_access_expr(CodeGen *g, ImportTableEntry *i |
| 1363 | 1411 | return_type = array_type->data.pointer.child_type; |
| 1364 | 1412 | } else { |
| 1365 | 1413 | 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))); | |
| 1367 | 1416 | } |
| 1368 | 1417 | return_type = g->builtin_types.entry_invalid; |
| 1369 | 1418 | } |
| ... | ... | @@ -2197,6 +2246,9 @@ static TypeTableEntry * analyze_expression(CodeGen *g, ImportTableEntry *import, |
| 2197 | 2246 | // for reading array access; assignment handled elsewhere |
| 2198 | 2247 | return_type = analyze_array_access_expr(g, import, context, node); |
| 2199 | 2248 | break; |
| 2249 | case NodeTypeSliceExpr: | |
| 2250 | return_type = analyze_slice_expr(g, import, context, node); | |
| 2251 | break; | |
| 2200 | 2252 | case NodeTypeFieldAccessExpr: |
| 2201 | 2253 | return_type = analyze_field_access_expr(g, import, context, node); |
| 2202 | 2254 | break; |
| ... | ... | @@ -2541,6 +2593,7 @@ static void analyze_top_level_declaration(CodeGen *g, ImportTableEntry *import, |
| 2541 | 2593 | case NodeTypeBinOpExpr: |
| 2542 | 2594 | case NodeTypeFnCallExpr: |
| 2543 | 2595 | case NodeTypeArrayAccessExpr: |
| 2596 | case NodeTypeSliceExpr: | |
| 2544 | 2597 | case NodeTypeNumberLiteral: |
| 2545 | 2598 | case NodeTypeStringLiteral: |
| 2546 | 2599 | case NodeTypeCharLiteral: |
src/analyze.hpp+2| ... | ... | @@ -355,9 +355,11 @@ struct CodeGenNode { |
| 355 | 355 | StructDeclNode struct_decl_node; // for NodeTypeStructDecl |
| 356 | 356 | FieldAccessNode field_access_node; // for NodeTypeFieldAccessExpr |
| 357 | 357 | CastNode cast_node; // for NodeTypeCastExpr |
| 358 | // note: I've been using this field on some non-number literal nodes too. | |
| 358 | 359 | NumberLiteralNode num_lit_node; // for NodeTypeNumberLiteral |
| 359 | 360 | VarDeclNode var_decl_node; // for NodeTypeVariableDeclaration |
| 360 | 361 | StructValFieldNode struct_val_field_node; // for NodeTypeStructValueField |
| 362 | // note: I've been using this field on some non-struct val expressions too. | |
| 361 | 363 | StructValExprNode struct_val_expr_node; // for NodeTypeStructValueExpr |
| 362 | 364 | IfVarNode if_var_node; // for NodeTypeStructValueExpr |
| 363 | 365 | 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) { |
| 215 | 215 | } |
| 216 | 216 | } |
| 217 | 217 | |
| 218 | static 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); | |
| 218 | static LLVMValueRef gen_array_base_ptr(CodeGen *g, AstNode *node) { | |
| 219 | TypeTableEntry *type_entry = get_expr_type(node); | |
| 223 | 220 | |
| 224 | 221 | 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); | |
| 227 | 224 | if (type_entry->id == TypeTableEntryIdPointer) { |
| 228 | 225 | // we have a double pointer so we must dereference it once |
| 229 | 226 | add_debug_source_node(g, node); |
| 230 | 227 | array_ptr = LLVMBuildLoad(g->builder, array_ptr, ""); |
| 231 | 228 | } |
| 232 | 229 | } else { |
| 233 | array_ptr = gen_expr(g, array_expr_node); | |
| 230 | array_ptr = gen_expr(g, node); | |
| 234 | 231 | } |
| 235 | 232 | |
| 236 | 233 | assert(LLVMGetTypeKind(LLVMTypeOf(array_ptr)) == LLVMPointerTypeKind); |
| 237 | 234 | |
| 235 | return array_ptr; | |
| 236 | } | |
| 237 | ||
| 238 | static 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 | ||
| 238 | 246 | LLVMValueRef subscript_value = gen_expr(g, node->data.array_access_expr.subscript); |
| 239 | 247 | assert(subscript_value); |
| 240 | 248 | |
| ... | ... | @@ -299,6 +307,48 @@ static LLVMValueRef gen_field_ptr(CodeGen *g, AstNode *node, TypeTableEntry **ou |
| 299 | 307 | return LLVMBuildStructGEP(g->builder, struct_ptr, codegen_field_access->field_index, ""); |
| 300 | 308 | } |
| 301 | 309 | |
| 310 | static 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 | ||
| 302 | 352 | static LLVMValueRef gen_array_access_expr(CodeGen *g, AstNode *node, bool is_lvalue) { |
| 303 | 353 | assert(node->type == NodeTypeArrayAccessExpr); |
| 304 | 354 | |
| ... | ... | @@ -1443,6 +1493,8 @@ static LLVMValueRef gen_expr_no_cast(CodeGen *g, AstNode *node) { |
| 1443 | 1493 | return gen_fn_call_expr(g, node); |
| 1444 | 1494 | case NodeTypeArrayAccessExpr: |
| 1445 | 1495 | return gen_array_access_expr(g, node, false); |
| 1496 | case NodeTypeSliceExpr: | |
| 1497 | return gen_slice_expr(g, node); | |
| 1446 | 1498 | case NodeTypeFieldAccessExpr: |
| 1447 | 1499 | return gen_field_access_expr(g, node, false); |
| 1448 | 1500 | case NodeTypeUnreachable: |
src/parser.cpp+42-8| ... | ... | @@ -90,6 +90,8 @@ const char *node_type_str(NodeType node_type) { |
| 90 | 90 | return "FnCallExpr"; |
| 91 | 91 | case NodeTypeArrayAccessExpr: |
| 92 | 92 | return "ArrayAccessExpr"; |
| 93 | case NodeTypeSliceExpr: | |
| 94 | return "SliceExpr"; | |
| 93 | 95 | case NodeTypeExternBlock: |
| 94 | 96 | return "ExternBlock"; |
| 95 | 97 | case NodeTypeDirective: |
| ... | ... | @@ -298,6 +300,14 @@ void ast_print(AstNode *node, int indent) { |
| 298 | 300 | ast_print(node->data.array_access_expr.array_ref_expr, indent + 2); |
| 299 | 301 | ast_print(node->data.array_access_expr.subscript, indent + 2); |
| 300 | 302 | 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; | |
| 301 | 311 | case NodeTypeDirective: |
| 302 | 312 | fprintf(stderr, "%s\n", node_type_str(node->type)); |
| 303 | 313 | break; |
| ... | ... | @@ -1381,9 +1391,10 @@ static AstNode *ast_parse_primary_expr(ParseContext *pc, int *token_index, bool |
| 1381 | 1391 | } |
| 1382 | 1392 | |
| 1383 | 1393 | /* |
| 1384 | SuffixOpExpression : PrimaryExpression option(FnCallExpression | ArrayAccessExpression | FieldAccessExpression) | |
| 1394 | SuffixOpExpression : PrimaryExpression option(FnCallExpression | ArrayAccessExpression | FieldAccessExpression | SliceExpression) | |
| 1385 | 1395 | FnCallExpression : token(LParen) list(Expression, token(Comma)) token(RParen) |
| 1386 | 1396 | ArrayAccessExpression : token(LBracket) Expression token(RBracket) |
| 1397 | SliceExpression : token(LBracket) Expression token(Ellipsis) option(Expression) token(RBracket) option(token(Const)) | |
| 1387 | 1398 | FieldAccessExpression : token(Dot) token(Symbol) |
| 1388 | 1399 | */ |
| 1389 | 1400 | static 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 |
| 1405 | 1416 | } else if (token->id == TokenIdLBracket) { |
| 1406 | 1417 | *token_index += 1; |
| 1407 | 1418 | |
| 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); | |
| 1411 | 1420 | |
| 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); | |
| 1415 | 1422 | |
| 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 | } | |
| 1417 | 1451 | } else if (token->id == TokenIdDot) { |
| 1418 | 1452 | *token_index += 1; |
| 1419 | 1453 |
src/parser.hpp+9| ... | ... | @@ -41,6 +41,7 @@ enum NodeType { |
| 41 | 41 | NodeTypePrefixOpExpr, |
| 42 | 42 | NodeTypeFnCallExpr, |
| 43 | 43 | NodeTypeArrayAccessExpr, |
| 44 | NodeTypeSliceExpr, | |
| 44 | 45 | NodeTypeFieldAccessExpr, |
| 45 | 46 | NodeTypeUse, |
| 46 | 47 | NodeTypeVoid, |
| ... | ... | @@ -181,6 +182,13 @@ struct AstNodeArrayAccessExpr { |
| 181 | 182 | AstNode *subscript; |
| 182 | 183 | }; |
| 183 | 184 | |
| 185 | struct AstNodeSliceExpr { | |
| 186 | AstNode *array_ref_expr; | |
| 187 | AstNode *start; | |
| 188 | AstNode *end; | |
| 189 | bool is_const; | |
| 190 | }; | |
| 191 | ||
| 184 | 192 | struct AstNodeFieldAccessExpr { |
| 185 | 193 | AstNode *struct_expr; |
| 186 | 194 | Buf field_name; |
| ... | ... | @@ -378,6 +386,7 @@ struct AstNode { |
| 378 | 386 | AstNodePrefixOpExpr prefix_op_expr; |
| 379 | 387 | AstNodeFnCallExpr fn_call_expr; |
| 380 | 388 | AstNodeArrayAccessExpr array_access_expr; |
| 389 | AstNodeSliceExpr slice_expr; | |
| 381 | 390 | AstNodeUse use; |
| 382 | 391 | AstNodeIfBoolExpr if_bool_expr; |
| 383 | 392 | AstNodeIfVarExpr if_var_expr; |
test/run_tests.cpp+29| ... | ... | @@ -907,6 +907,35 @@ pub fn main(argc: isize, argv: &&u8, env: &&u8) -> i32 { |
| 907 | 907 | "min i16: -32768\n" |
| 908 | 908 | "min i32: -2147483648\n" |
| 909 | 909 | "min i64: -9223372036854775808\n"); |
| 910 | ||
| 911 | ||
| 912 | add_simple_case("slicing", R"SOURCE( | |
| 913 | use "std.zig"; | |
| 914 | pub 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 | ||
| 910 | 939 | } |
| 911 | 940 | |
| 912 | 941 | //////////////////////////////////////////////////////////////////////////////////// |