diff --git a/grammar.ebnf b/grammar.ebnf index 97bb513..5d4d9d4 100644 --- a/grammar.ebnf +++ b/grammar.ebnf @@ -174,7 +174,10 @@ continue_statement = "continue" ; expression = assignment_expression | struct_literal | array_literal - | tuple_literal ; + | tuple_literal + | array_initializer; + +array_initializer = [ integer_constant ] arra_type ; assignment_expression = logical_or_expression, [ "=", assignment_expression ] ; diff --git a/run_tests.sh b/run_tests.sh index d0c79a5..b094d66 100755 --- a/run_tests.sh +++ b/run_tests.sh @@ -1,7 +1,8 @@ #!/usr/bin/env bash # Zinc test runner -# Usage: ./run_tests.sh [--zinc ] [--tests ] +# Usage: ./run_tests.sh [] [--zinc ] [--tests ] # +# If is given, only run tests whose filename starts with that number. # Exit codes: # 0 - all pass/fail tests succeeded # 1 - one or more pass/fail tests failed @@ -14,6 +15,13 @@ PASS_DIR="$TESTS_DIR/pass" FAIL_DIR="$TESTS_DIR/fail" XFAIL_DIR="$TESTS_DIR/xfail" +# Optional test number filter (first argument if it looks like a number) +TEST_FILTER="" +if [[ "${1:-}" =~ ^[0-9]+$ ]]; then + TEST_FILTER="$1" + shift +fi + GREEN='\033[0;32m' RED='\033[0;31m' YELLOW='\033[0;33m' @@ -173,30 +181,43 @@ if [ ! -x "$ZINC" ]; then fi echo -e "${BOLD}Zinc test suite${NC} (zinc: $ZINC)" +[ -n "$TEST_FILTER" ] && echo -e " filter: tests starting with ${BOLD}${TEST_FILTER}${NC}" echo "" if [ -d "$PASS_DIR" ] && compgen -G "$PASS_DIR/*.zn" > /dev/null 2>&1; then - echo -e "${BOLD}Pass tests${NC}" + ran=0 for f in "$PASS_DIR"/*.zn; do + name=$(basename "$f") + [ -n "$TEST_FILTER" ] && [[ "$name" != ${TEST_FILTER}_* ]] && continue + [ "$ran" -eq 0 ] && echo -e "${BOLD}Pass tests${NC}" + ran=1 run_pass_test "$f" done - echo "" + [ "$ran" -eq 1 ] && echo "" fi if [ -d "$FAIL_DIR" ] && compgen -G "$FAIL_DIR/*.zn" > /dev/null 2>&1; then - echo -e "${BOLD}Fail tests${NC} (compiler must reject these)" + ran=0 for f in "$FAIL_DIR"/*.zn; do + name=$(basename "$f") + [ -n "$TEST_FILTER" ] && [[ "$name" != ${TEST_FILTER}_* ]] && continue + [ "$ran" -eq 0 ] && echo -e "${BOLD}Fail tests${NC} (compiler must reject these)" + ran=1 run_fail_test "$f" done - echo "" + [ "$ran" -eq 1 ] && echo "" fi if [ -d "$XFAIL_DIR" ] && compgen -G "$XFAIL_DIR/*.zn" > /dev/null 2>&1; then - echo -e "${BOLD}Expected failures${NC} (unimplemented features)" + ran=0 for f in "$XFAIL_DIR"/*.zn; do + name=$(basename "$f") + [ -n "$TEST_FILTER" ] && [[ "$name" != ${TEST_FILTER}_* ]] && continue + [ "$ran" -eq 0 ] && echo -e "${BOLD}Expected failures${NC} (unimplemented features)" + ran=1 run_xfail_test "$f" done - echo "" + [ "$ran" -eq 1 ] && echo "" fi total=$((passed + failed)) diff --git a/tests/pass/15_array_subscript.zn b/tests/pass/15_array_subscript.zn index cd6ec5f..0ed89bd 100644 --- a/tests/pass/15_array_subscript.zn +++ b/tests/pass/15_array_subscript.zn @@ -1,6 +1,9 @@ foreign u0 printf(*char) u32 main() { + + chars := [65, 66, 67, 68] as []char + arr := ["a", "b", "c", "d"] arr[0] = "first" arr[3] = "last" diff --git a/tests/pass/21_array_initializer.ezpected b/tests/pass/21_array_initializer.ezpected new file mode 100644 index 0000000..9dfcf39 --- /dev/null +++ b/tests/pass/21_array_initializer.ezpected @@ -0,0 +1,5 @@ +0 +1 +2 +3 +4 diff --git a/tests/pass/21_array_initializer.zn b/tests/pass/21_array_initializer.zn new file mode 100644 index 0000000..d2fd312 --- /dev/null +++ b/tests/pass/21_array_initializer.zn @@ -0,0 +1,25 @@ +foreign u0 printf(*char) +foreign i32 putchar(i32) + +i32 main() { + arr := [5]i32 + + arr[0] = 1 + + i := 0 + for i < 5 { + arr[i] = i + i = i + 1 + } + + i = 0 + for i < 5 { + putchar(48 + arr[i]) + putchar(10) + i = i + 1 + } + + arr[5] + + return 0 +} diff --git a/tests/xfail/04_array_type.zn b/tests/xfail/04_array_type.zn deleted file mode 100644 index c42fb1a..0000000 --- a/tests/xfail/04_array_type.zn +++ /dev/null @@ -1,6 +0,0 @@ -foreign u0 printf(*char) - -i32 main() { - []char arr = [65, 66, 67, 68, 69] as []char - return 0 -} diff --git a/zgen.c b/zgen.c index 47a60ed..00634dc 100644 --- a/zgen.c +++ b/zgen.c @@ -948,6 +948,14 @@ static LLVMValueRef genMemberAccess(ZCodegen *ctx, ZNode *node) { ); } +static LLVMValueRef genArrayInit(ZCodegen *ctx, ZNode *node) { + ZLLVMStack *stack = getStackValue(ctx, node); + + if (!stack) return NULL; + + return stack->stack; +} + static LLVMValueRef genExpr(ZCodegen *ctx, ZNode *node) { switch (node->type) { case NODE_STRUCT_LIT: return genStructLit (ctx, node); @@ -961,6 +969,7 @@ static LLVMValueRef genExpr(ZCodegen *ctx, ZNode *node) { case NODE_MEMBER: return genMemberAccess (ctx, node); case NODE_BINARY: return genBinary (ctx, node); case NODE_UNARY: return genUnary (ctx, node); + case NODE_ARRAY_INIT: return genArrayInit (ctx, node); case NODE_SIZEOF: { usize size = typeSize(ctx, node->sizeofExpr.type); @@ -1195,7 +1204,6 @@ static void genStmt(ZCodegen *ctx, ZNode *stmt) { /* Variable already declared at the start of the function*/ case NODE_VAR_DECL: genVarDecl (ctx, stmt); break; case NODE_RETURN: genRet (ctx, stmt); break; - case NODE_BINARY: genExpr (ctx, stmt); break; case NODE_CALL: genCall (ctx, stmt); break; case NODE_IF: genIf (ctx, stmt); break; case NODE_BLOCK: genBlock (ctx, stmt); break; @@ -1204,11 +1212,14 @@ static void genStmt(ZCodegen *ctx, ZNode *stmt) { case NODE_BREAK: genBreak (ctx, stmt); break; case NODE_CONTINUE: genContinue (ctx, stmt); break; case NODE_DEFER: genDefer (ctx, stmt); break; - default: - printf("Node '%d' does not compile yet\n", stmt->type); + default: { + LLVMValueRef compiled = genExpr(ctx, stmt); + if (compiled) return; error(ctx->state, stmt->tok, "Node '%d' does not compile yet", stmt->type); + break; + } } } diff --git a/zinc.h b/zinc.h index 327ae8f..56bb7e0 100644 --- a/zinc.h +++ b/zinc.h @@ -121,6 +121,7 @@ typedef enum { NODE_STRUCT_LIT, NODE_TUPLE_LIT, NODE_ARRAY_LIT, + NODE_ARRAY_INIT, NODE_MACRO, NODE_GOTO, NODE_LABEL, @@ -379,6 +380,8 @@ struct ZNode { ZNode **arraylit; + ZType *arrayinit; + struct { ZToken *ident; ZNode **fields; diff --git a/zmod.c b/zmod.c index 9c3e796..2e9e048 100644 --- a/zmod.c +++ b/zmod.c @@ -12,9 +12,10 @@ static char *nodeLabels[] = { "VAR_DECL", "BINARY", "UNARY", "CALL", "FUNC", "LITERAL", "IDENTIFIER", "STRUCT", "SUBSCRIPT", "MEMBER", "MODULE", "UNION", "FIELD", "TYPEDEF", "FOREIGN", - "DEFER", "STRUCT_LIT", "TUPLE_LIT", "ARRAY_LIT", "MACRO", - "GOTO", "LABEL", "TYPE", "ENUM", "BREAK", - "CONTINUE", "ENUM_FIELD", "CAST", "SIZEOF", "STATIC_ACCESS" + "DEFER", "STRUCT_LIT", "TUPLE_LIT", "ARRAY_LIT", "ARRAY_INIT", + "MACRO", "GOTO", "LABEL", "TYPE", "ENUM", + "BREAK", "CONTINUE", "ENUM_FIELD", "CAST", "SIZEOF", + "STATIC_ACCESS" }; diff --git a/zparse.c b/zparse.c index f8b2557..6e1c587 100644 --- a/zparse.c +++ b/zparse.c @@ -48,6 +48,7 @@ static ZNode *parseBinary (ZParser *); static ZNode *parseContinue (ZParser *); static ZNode *parseArrayLit (ZParser *); static ZNode *parseTupleLit (ZParser *); +static ZType *parseTypeArray (ZParser *); static ZNode *parseStructLit (ZParser *); static ZNode *parseVarInferred (ZParser *); static ZNode *parseVarDefTyped (ZParser *); @@ -276,6 +277,18 @@ static ZNode *parseGenericBinary(ZParser *parser, return node ? node : left; } +static ZNode *parseArrayInit(ZParser *parser) { + ZType *arr = parseTypeArray(parser); + + guard(arr); + guard(arr->array.size > 0); + + ZNode *node = makenode(NODE_ARRAY_INIT); + node->arrayinit = arr; + + return node; +} + static ZNode *parsePrimary(ZParser *parser) { ZToken *start = peek(parser); guard(start); @@ -293,6 +306,10 @@ static ZNode *parsePrimary(ZParser *parser) { expect(parser, TOK_RPAREN); return node; } else if (check(parser, TOK_LSBRACKET)) { + return parseOrGrammar(parser, (ZParseFunc[]){ + parseArrayInit, + parseArrayLit + }, 2); return parseArrayLit(parser); } else if (check(parser, TOK_IDENT)) { if (checkAhead(parser, TOK_DOUBLE_COLON, 1)) { diff --git a/zsem.c b/zsem.c index 9f41750..9ebd7bc 100644 --- a/zsem.c +++ b/zsem.c @@ -914,6 +914,12 @@ static ZType *resolveTupleLiteral(ZSemantic *semantic, ZNode *node) { return result; } +static ZType *resolveArrayInit(ZSemantic *semantic, ZNode *node) { + node->arrayinit = resolveTypeRef(semantic, node->arrayinit); + node->resolved = node->arrayinit; + return node->arrayinit; +} + /* * Resolve the type of any expression node and cache the result in node->resolved. * Returns the resolved ZType* or NULL on error. @@ -925,15 +931,16 @@ ZType *resolveType(ZSemantic *semantic, ZNode *curr) { ZType *result = NULL; switch (curr->type) { - case NODE_CALL: result = resolveFuncCall(semantic, curr); break; - case NODE_LITERAL: result = resolveLiteralType(curr); break; + case NODE_CALL: result = resolveFuncCall (semantic, curr); break; + case NODE_LITERAL: result = resolveLiteralType (curr); break; case NODE_MEMBER: result = resolveMemberAccess(semantic, curr); break; case NODE_SUBSCRIPT: result = resolveArrSubscript(semantic, curr); break; - case NODE_STRUCT_LIT: result = resolveStructLit(semantic, curr); break; - case NODE_IDENTIFIER: result = resolveIdentifier(semantic, curr); break; + case NODE_STRUCT_LIT: result = resolveStructLit (semantic, curr); break; + case NODE_IDENTIFIER: result = resolveIdentifier (semantic, curr); break; case NODE_ARRAY_LIT: result = resolveArrayLiteral(semantic, curr); break; case NODE_TUPLE_LIT: result = resolveTupleLiteral(semantic, curr); break; - case NODE_BINARY: result = resolveBinary(semantic, curr); break; + case NODE_BINARY: result = resolveBinary (semantic, curr); break; + case NODE_ARRAY_INIT: result = resolveArrayInit (semantic, curr); break; case NODE_UNARY: { ZType *operand = resolveType(semantic, curr->unary.operand);