/* Unit tests of parse.c Copyright © 2021-2024 Samuel Lidén Borell Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include "../parse.c" #define SLULPARSE #include "parsecommon.h" #include "unittest.h" #include "alltests.h" #include "testcommon.h" #include #define IMPLQUALS Q_CLOSED /* Identifier macros */ #define ASSERT_LINECOL(_ident, _line, _col) do { \ tsoftassert((_ident)->line == (_line)); \ tsoftassert((_ident)->column == (_col)); \ } while (0) #define ASSERT_IDENT(_ident, _name) do { \ if (tsoftassert((_ident)->length == strlen(_name))) { \ tsoftassert(!strncmp(node_nameptr(_ident), (_name), sizeof(_name)-1)); \ } \ } while (0) #define ASSERT_IDENT_L(_ident, _line, _col, _name) do { \ ASSERT_LINECOL(_ident, _line, _col); \ ASSERT_IDENT(_ident, _name); \ } while (0) /* Type macros */ #define ASSERT_TYPE_ELEMENTARY(_type, _bt) do { \ if (tsoftassert((_type)->type == T_ELMNTRY)) { \ tsoftassert(((_type)->quals & ~IMPLQUALS) == 0); \ tsoftassert((_type)->misc == 0); \ tsoftassert((_type)->u.builtin == (_bt)); \ } \ } while (0) #define ASSERT_TYPE_BOOL(_type) ASSERT_TYPE_ELEMENTARY((_type), BT_Bool) #define ASSERT_TYPE_BYTE(_type) ASSERT_TYPE_ELEMENTARY((_type), BT_Byte) #define ASSERT_TYPE_INT(_type) ASSERT_TYPE_ELEMENTARY((_type), BT_Int) #define ASSERT_TYPE_ARRAY(_type, _length) do { \ struct ArrayType *arr; \ tassert((_type)->type == T_ARRAY); \ tsoftassert(((_type)->quals & ~IMPLQUALS) == 0); \ tassert((arr = (_type)->u.arr) != NULL); \ tassert(arr->lengthexpr != NULL); \ tassert(arr->lengthexpr->root != NULL); \ tsoftassert(arr->lengthexpr->root == arr->lengthexpr->rpn); \ tassert(arr->lengthexpr->root->exprtype == E_INTEGER); \ tsoftassert(arr->lengthexpr->root->a.intval == (_length)); \ } while (0) #define ASSERT_TYPE_REF(_type, _quals, _reftype) do { \ tassert((_type)->type == T_REF); \ tsoftassert((_type)->misc == (_reftype)); \ tsoftassert((_type)->quals == (_quals)); \ tassert((_type)->u.nested != NULL); \ } while (0) #define ASSERT_TYPE_IDENT(_type, _quals, _refdtype) do { \ tassert((_type)->type == T_IDENT); \ tsoftassert((_type)->quals == (_quals)); \ tassert((_type)->u.ident == (_refdtype)); \ } while (0) #define ASSERT_TYPE_PRIVATE(_type) do { \ tassert((_type)->type == T_PRIVATE); \ tsoftassert(((_type)->quals & ~IMPLQUALS) == 0); \ tsoftassert((_type)->misc == 0); \ } while (0) /* RPN expression macros */ #define ASSERT_RPN(tok, c) do { \ tassert(rpn != NULL); \ tsoftassert(rpn->rpntokentype == (tok)); \ tsoftassert(rpn->rpncontext == (c)); \ rpn = rpn->rpnnext; \ } while (0) #define ASSERT_RPN_INTEGER(num) do { \ tassert(rpn != NULL); \ tsoftassert(rpn->rpntokentype == CSLUL_T_Integer); \ tsoftassert(rpn->rpncontext == RC_TERMINAL); \ tsoftassert(rpn->exprtype == E_INTEGER); \ tsoftassert(rpn->a.intval == (num)); \ rpn = rpn->rpnnext; \ } while (0) #define ASSERT_RPN_IDENT(identname) do { \ ASSERT_EXPR_IDENT((rpn), (identname)); \ rpn = rpn->rpnnext; \ } while (0) #define ASSERT_RPN_FIELDORTYPEIDENT(_exprtype, _rpncontext, identname) do { \ tassert(rpn != NULL); \ tsoftassert(rpn->exprtype == (_exprtype)); \ tsoftassert(rpn->rpntokentype == CSLUL_T_Dot); \ tsoftassert(rpn->rpncontext == (_rpncontext)); \ if (tsoftassert(rpn->b.unbound_ident != NULL)) { \ tsoftassert(!strcmp(rpn->b.unbound_ident, (identname))); \ } \ rpn = rpn->rpnnext; \ } while (0) #define ASSERT_RPN_FIELD(identname) \ ASSERT_RPN_FIELDORTYPEIDENT(E_FIELD, RC_UNARY, (identname)) #define ASSERT_RPN_TYPEIDENT(identname) \ ASSERT_RPN_FIELDORTYPEIDENT(E_TYPEIDENT, RC_TERMINAL, (identname)) #define ASSERT_RPN_THIS(_class) do { \ tassert(rpn != NULL); \ tsoftassert(rpn->rpntokentype == CSLUL_T_KW_This); \ tsoftassert(rpn->rpncontext == RC_TERMINAL); \ tsoftassert(rpn->exprtype == E_THIS); \ tsoftassert(rpn->a.thisclass == (_class)); \ rpn = rpn->rpnnext; \ } while (0) #define ASSERT_RPN_END tsoftassert(rpn == NULL) /* Expression macros */ #define ASSERT_EXPR_INT(expr, intconst) do { \ struct ExprNode *rpn; \ tassert((expr) != NULL); \ tassert((expr)->root != NULL); \ tassert((expr)->root == (expr)->rpn); \ rpn = (expr)->rpn; \ ASSERT_RPN_INTEGER(intconst); \ ASSERT_RPN_END; \ } while (0) #define ASSERT_EXPR_TYPEIDENT(expr, _typeident) do { \ struct ExprNode *rpn; \ tassert((expr) != NULL); \ tassert((expr)->root != NULL); \ tassert((expr)->root == (expr)->rpn); \ rpn = (expr)->rpn; \ ASSERT_RPN_TYPEIDENT((_typeident)); \ ASSERT_RPN_END; \ } while (0) #define ASSERT_EXPR_INT_EQ(expr, intconst1, intconst2) do { \ struct ExprNode *rpn; \ tassert((expr) != NULL); \ tassert((expr)->root != NULL); \ tassert((expr)->root->exprtype == E_BINARYOP); \ rpn = (expr)->rpn; \ ASSERT_RPN_INTEGER(intconst1); \ ASSERT_RPN_INTEGER(intconst2); \ tsoftassert(rpn == (expr)->root); \ ASSERT_RPN(CSLUL_T_Equal, RC_OP); \ ASSERT_RPN_END; \ } while (0) #define ASSERT_EXPR_IDENT(expr, identname) do { \ tassert((expr) != NULL); \ tsoftassert((expr)->rpntokentype == CSLUL_T_LowerIdent); \ tsoftassert((expr)->rpncontext == RC_TERMINAL); \ if (tsoftassert((expr)->exprtype == E_IDENT) && \ tsoftassert((expr)->a.ident != NULL)) { \ ASSERT_IDENT((expr)->a.ident, identname); \ } \ } while (0) /* Statement macros */ #define ASSERT_STMT_NOP(s) do { \ tassert((s) != NULL); \ tassert((s)->type == S_NOP); \ } while (0) #define ASSERT_STMT_IF(s) do { \ tassert((s) != NULL); \ tassert((s)->type == S_IF); \ tassert((s)->u.ifstm != NULL); \ tassert((s)->u.ifstm->cond != NULL); \ tsoftassert((s)->u.ifstm->false_block == NULL); \ } while (0) #define ASSERT_STMT_IF_WITH_ELSE(s) do { \ tassert((s) != NULL); \ tassert((s)->type == S_IF); \ tassert((s)->u.ifstm != NULL); \ tassert((s)->u.ifstm->cond != NULL); \ tassert((s)->u.ifstm->false_block != NULL); \ } while (0) #define ASSERT_STMT_WHILE_X(s, opempty, opend) do { \ tassert((s) != NULL); \ tassert((s)->type == S_WHILE); \ tassert((s)->u.whilestm != NULL); \ tassert((s)->u.whilestm->cond != NULL); \ tassert((s)->u.whilestm->l.empty_block opempty NULL); \ tassert((s)->u.whilestm->l.end_block opend NULL); \ } while (0) #define ASSERT_STMT_WHILE(s) ASSERT_STMT_WHILE_X((s), ==, ==) #define ASSERT_STMT_WHILE_LOOPEMPTY(s) ASSERT_STMT_WHILE_X((s), !=, ==) #define ASSERT_STMT_WHILE_LOOPEND(s) ASSERT_STMT_WHILE_X((s), ==, !=) #define ASSERT_STMT_WHILE_LOOPEMPTYEND(s) ASSERT_STMT_WHILE_X((s), !=, !=) #define ASSERT_STMT_DOWHILE_X(s, opempty, opend) do { \ tassert((s) != NULL); \ tassert((s)->type == S_DOWHILE); \ tassert((s)->u.whilestm != NULL); \ tassert((s)->u.whilestm->cond != NULL); \ tassert((s)->u.whilestm->l.empty_block opempty NULL); \ tassert((s)->u.whilestm->l.end_block opend NULL); \ } while (0) #define ASSERT_STMT_DOWHILE(s) ASSERT_STMT_DOWHILE_X((s), ==, ==) #define ASSERT_STMT_DOWHILE_LOOPEMPTY(s) ASSERT_STMT_DOWHILE_X((s), !=, ==) #define ASSERT_STMT_DOWHILE_LOOPEND(s) ASSERT_STMT_DOWHILE_X((s), ==, !=) #define ASSERT_STMT_DOWHILE_LOOPEMPTYEND(s) ASSERT_STMT_DOWHILE_X((s), !=, !=) #define ASSERT_STMT_FOR_X(s, i, opempty, opend) do { \ tassert((s) != NULL); \ tassert((s)->type == S_FOR); \ tassert((s)->u.forstm != NULL); \ tassert((s)->u.forstm->loopexpr != NULL); \ tassert((s)->u.forstm->loopvar.type.type != 0); \ tassert((s)->u.forstm->loopvar.ident.length != 0); \ if (tsoftassert((s)->u.forstm->loopvar.ident.length == sizeof(i)-1)) { \ tsoftassert(!strncmp(node_nameptr(&(s)->u.forstm->loopvar.ident), \ (i), sizeof(i)-1)); \ } \ tassert((s)->u.forstm->l.empty_block opempty NULL); \ tassert((s)->u.forstm->l.end_block opend NULL); \ } while (0) #define ASSERT_STMT_FOR(s,i) ASSERT_STMT_FOR_X((s), (i), ==, ==) #define ASSERT_STMT_FOR_LOOPEMPTY(s,i) ASSERT_STMT_FOR_X((s), (i), !=, ==) #define ASSERT_STMT_FOR_LOOPEND(s,i) ASSERT_STMT_FOR_X((s), (i), ==, !=) #define ASSERT_STMT_FOR_LOOPEMPTYEND(s,i) ASSERT_STMT_FOR_X((s), (i), !=, !=) #define ASSERT_STMT_BREAK(s, li) do { \ tassert((s) != NULL); \ tassert((s)->type == S_BREAK); \ tsoftassert((s)->u.loopinfo == (li)); \ } while (0) #define ASSERT_STMT_CONTINUE(s, li) do { \ tassert((s) != NULL); \ tassert((s)->type == S_CONT); \ tsoftassert((s)->u.loopinfo == (li)); \ } while (0) #define ASSERT_STMT_RETURN(s) do { \ tassert((s) != NULL); \ tassert((s)->type == S_RETURN); \ } while (0) #define ASSERT_STMT_RETURN_INT(s, intconst) do { \ ASSERT_STMT_RETURN(s); \ tassert((s)->u.expr != NULL); \ ASSERT_EXPR_INT((s)->u.expr, intconst); \ } while (0) #define ASSERT_GOTOIDENT(s, _name) do { \ tassert((s)->u.gotoident != NULL); \ tassert((s)->u.gotoident->seen_line != 0); \ tassert((s)->u.gotoident->seen_column != 0); \ tassert((s)->u.gotoident->node.length == sizeof(_name)-1); \ tassert(node_nameptr(&(s)->u.gotoident->node) != NULL); \ tsoftassert(!strncmp(node_nameptr(&(s)->u.gotoident->node), (_name), \ sizeof(_name)-1)); \ } while (0) #define ASSERT_STMT_GOTO(s, _name) do { \ tassert((s) != NULL); \ tassert((s)->type == S_GOTO); \ ASSERT_GOTOIDENT((s), (_name)); \ } while (0) #define ASSERT_STMT_TARGET(s, _name) do { \ tassert((s) != NULL); \ tassert((s)->type == S_TARGET); \ ASSERT_GOTOIDENT((s), (_name)); \ } while (0) #define ASSERT_STMT_SWITCH(s) do { \ tassert((s) != NULL); \ tassert((s)->type == S_SWITCH); \ tassert((s)->u.switchstm != NULL); \ tassert((s)->u.switchstm->cond != NULL); \ } while (0) #define ASSERT_STMT_SUBCASE_INT(s, i) do { \ tassert((s) != NULL); \ tassert((s)->type == S_SUBCASE); \ tassert((s)->u.subcase != NULL); \ ASSERT_EXPR_INT((s)->u.subcase->value, (i)); \ } while (0) #define ASSERT_STMT_ASSERT_EQ(s, intconst1, intconst2) do { \ tassert((s) != NULL); \ tassert((s)->type == S_ASSERT); \ tassert((s)->u.expr != NULL); \ ASSERT_EXPR_INT_EQ((s)->u.expr, (intconst1), (intconst2)); \ } while (0) #define ASSERT_STMT_BLOCK(s) do { \ tassert((s) != NULL); \ tassert((s)->type == S_BLOCK); \ tassert((s)->u.block != NULL); \ } while (0) #define ASSERT_STMT_DECL(s, identname) do { \ tassert((s) != NULL); \ tassert((s)->type == S_DECL); \ tassert((s)->u.vardef != NULL); \ if (tsoftassert((s)->u.vardef->decl.ident.length == strlen(identname))) { \ tsoftassert(!strncmp(node_nameptr(&(s)->u.vardef->decl.ident), (identname), (s)->u.vardef->decl.ident.length)); \ } \ } while (0) /** Runs before each test case */ static void before_test(void) { set_mhparse(0); /* Set mode. */ } /** Runs after each test case */ static void after_test(void) { verify_errors(); } static void test_parse_ast_constants(void) { /* These might get optimized out. (void) prevents a warning */ (void)tsoftassert(BT_NUMTYPES == CSLUL_T_LastElemType-CSLUL_T_FirstElemType+1); (void)tsoftassert(BT_Bool == CSLUL_T_KW_Bool-CSLUL_T_FirstElemType); (void)tsoftassert(BT_String == CSLUL_T_KW_String-CSLUL_T_FirstElemType); (void)tsoftassert(BT_WUInt64 == CSLUL_T_KW_WUInt64-CSLUL_T_FirstElemType); } static void test_parse_declsize(void) { tsoftassert(get_declsize(0) == sizeof(struct TopLevelIdent)); tsoftassert(get_declsize(PI_LOCAL) == sizeof(struct IdentDecl)); tsoftassert(get_declsize(PI_TYPE) == sizeof(struct TopLevelType)); tsoftassert(get_declsize(PI_TYPE|PI_LOCAL) == sizeof(struct TypeDecl)); } static void test_parse_toplevel_data(void) { struct CSlul *ctx = create_ctx(CSLUL_P_IFACE); struct IdentDecl *decl; /* TODO const vs immutable. At the top level everything will be const (and constexpr-initialized). Inside functions, we need to distinguish between constexpr-initialized const and runtime-initialized const => We need different keywords. */ TEST_SOURCE("since 1.0\n" "data int i\n", 1); tsoftassert(!ctx->tl.types_root); tassert(ctx->tl.idents_root); ASSERT_LINECOL(ctx->tl.idents_root, 2, 10); tsoftassert(!ctx->tl.idents_root->lower); tsoftassert(!ctx->tl.idents_root->higher); tsoftassert(ctx->tl.idents_root->length == 1); tsoftassert(ctx->tl.idents_root->name.copy[0] == 'i'); decl = (struct IdentDecl*)ctx->tl.idents_root; ASSERT_TYPE_INT(&decl->type); tsoftassert(!decl->u.initval); free_ctx(ctx); } static void test_parse_toplevel_data_initval(void) { struct CSlul *ctx = create_ctx(CSLUL_P_IMPL); struct IdentDecl *decl; struct ExprNode *rpn; TEST_SOURCE("data int i = 1+2\n", 1); tsoftassert(!ctx->tl.types_root); tassert(ctx->tl.idents_root); ASSERT_LINECOL(ctx->tl.idents_root, 1, 10); tsoftassert(!ctx->tl.idents_root->lower); tsoftassert(!ctx->tl.idents_root->higher); tsoftassert(ctx->tl.idents_root->length == 1); tsoftassert(ctx->tl.idents_root->name.copy[0] == 'i'); decl = (struct IdentDecl*)ctx->tl.idents_root; ASSERT_TYPE_INT(&decl->type); tassert(decl->u.initval != NULL); tassert(decl->u.initval->rpn != NULL); rpn = decl->u.initval->rpn; ASSERT_RPN_INTEGER(1); ASSERT_RPN_INTEGER(2); tassert(decl->u.initval->root == rpn); /* Plus operation */ ASSERT_RPN(CSLUL_T_Plus, RC_OP); ASSERT_RPN_END; free_ctx(ctx); } static void test_parse_toplevel_data_versioned(void) { struct CSlul *ctx = create_ctx(CSLUL_P_IFACE); struct TopLevelIdent *i; struct ExprNode *rpn; struct ApiDef *ver11; TEST_SOURCE("since 1.1\n" "data int i = 1+2\n", 1); tsoftassert(!ctx->tl.types_root); tassert(ctx->tl.idents_root); tsoftassert(!ctx->tl.idents_root->lower); tsoftassert(!ctx->tl.idents_root->higher); tsoftassert(ctx->tl.idents_root->length == 1); tsoftassert(ctx->tl.idents_root->name.copy[0] == 'i'); i = (struct TopLevelIdent*)ctx->tl.idents_root; tassert(i != NULL); ASSERT_LINECOL(&i->decl.ident, 2, 10); tsoftassert(i->decl.type.defflags == D_DEFINED); ASSERT_TYPE_INT(&i->decl.type); ver11 = (struct ApiDef *)lookup(ctx, ctx->module.apidefs_root, "1.1"); tassert(ver11 != NULL); tassert(i->sinceversions != NULL); tsoftassert(i->sinceversions->next == NULL); tsoftassert(i->sinceversions->version == ver11); tassert(i->decl.u.initval != NULL); tassert(i->decl.u.initval->rpn != NULL); rpn = i->decl.u.initval->rpn; ASSERT_RPN_INTEGER(1); ASSERT_RPN_INTEGER(2); tassert(i->decl.u.initval->root == rpn); /* Plus operation */ ASSERT_RPN(CSLUL_T_Plus, RC_OP); ASSERT_RPN_END; free_ctx(ctx); } static void test_parse_toplevel_data_dupl1(void) { struct CSlul *ctx = create_ctx(CSLUL_P_IFACE); expect_error(CSLUL_E_IDENTEXISTS, 4, 10); TEST_SOURCE("since 1.0\n" "data int i\n" "since 1.0\n" "data int i\n", 1); free_ctx(ctx); } static void test_parse_toplevel_data_dupl2(void) { struct CSlul *ctx = create_ctx(CSLUL_P_IMPL); expect_error(CSLUL_E_IDENTEXISTS, 2, 10); TEST_SOURCE("data int i = 0\ndata int i = 0\n", 1); free_ctx(ctx); } static void test_parse_toplevel_data_badtype(void) { struct CSlul *ctx = create_ctx(CSLUL_P_IMPL); expect_error(CSLUL_E_BADTYPE, 1, 6); TEST_SOURCE("data if i = 0\n", 1); free_ctx(ctx); } static void test_parse_toplevel_data_noident(void) { struct CSlul *ctx = create_ctx(CSLUL_P_IMPL); expect_error(CSLUL_E_IDENTIFIEREXPECTED, 1, 10); TEST_SOURCE("data int 0\n", 1); free_ctx(ctx); } static void test_parse_toplevel_data_noequals(void) { struct CSlul *ctx = create_ctx(CSLUL_P_IMPL); expect_error(CSLUL_E_BADINITVALTOKEN, 1, 12); TEST_SOURCE("data int i 0\n", 1); free_ctx(ctx); } static void test_parse_toplevel_data_badcase1(void) { struct CSlul *ctx = create_ctx(CSLUL_P_IFACE); expect_error(CSLUL_E_CAPITALIZEDNONTYPE, 2, 10); TEST_SOURCE("since 1.2\n" "data int I\n", 1); tsoftassert(ctx->typedepth == -1); free_ctx(ctx); } static void test_parse_toplevel_data_badqual1(void) { struct CSlul *ctx = create_ctx(CSLUL_P_IMPL); expect_error(CSLUL_E_BADTLDATAQUAL, 1, 6); TEST_SOURCE("data var int i = 0\n", 1); free_ctx(ctx); } static void test_parse_toplevel_data_badqual2(void) { struct CSlul *ctx = create_ctx(CSLUL_P_IMPL); expect_error(CSLUL_E_BADTLDATAQUAL, 1, 6); TEST_SOURCE("data aliased int i = 0\n", 1); free_ctx(ctx); } static void test_parse_toplevel_data_badref(void) { struct CSlul *ctx = create_ctx(CSLUL_P_IFACE); expect_error(CSLUL_E_BADTLDATAREF, 2, 6); TEST_SOURCE("since 1.0\n" "data ref T t\n", 1); free_ctx(ctx); } static void test_parse_toplevel_data_missinginitval1(void) { struct CSlul *ctx = create_ctx(CSLUL_P_IMPL); /* Initial value is required in impl files */ expect_error(CSLUL_E_IMPLDATAWITHOUTINITVAL, 1, 11); TEST_SOURCE("data int i\n", 1); free_ctx(ctx); } static void test_parse_toplevel_data_missinginitval2(void) { struct CSlul *ctx = create_ctx(CSLUL_P_IMPL); expect_error(CSLUL_E_IMPLDATAWITHOUTINITVAL, 1, 11); TEST_SOURCE("data int i\n" "data ", 0); free_ctx(ctx); } static void test_parse_toplevel_data_missingsince(void) { /* XXX should data be allowed in interfaces at all? */ struct CSlul *ctx = create_ctx(CSLUL_P_IFACE); expect_error(CSLUL_E_MISSINGVERWITHAPIDEF, 1, 1); TEST_SOURCE("data int i\n", 1); tsoftassert(ctx->typedepth == -1); free_ctx(ctx); } static void test_parse_toplevel_data_badversion1(void) { struct CSlul *ctx = create_ctx(CSLUL_P_IFACE); expect_error(CSLUL_E_NOSINCEVERSION, 1, 6); TEST_SOURCE("since\n", 1); tsoftassert(ctx->typedepth == -1); free_ctx(ctx); } static void test_parse_toplevel_data_badversion2(void) { struct CSlul *ctx = create_ctx(CSLUL_P_IFACE); /* TODO report only one error */ expect_error(CSLUL_E_MISSINGVERWITHAPIDEF, 2, 1); expect_error(CSLUL_E_BADTOPLEVELCONTINUATION, 1, 7); expect_error(CSLUL_E_NOSINCEVERSION, 1, 7); TEST_SOURCE("since a\ndata int i\n", 1); tsoftassert(ctx->typedepth == -1); free_ctx(ctx); } static void test_parse_toplevel_data_versionwithoutapidef(void) { struct CSlul *ctx = create_ctx(CSLUL_P_IFACE); ctx->parsed_module->num_apidefs = 0; ctx->parsed_module->first_apidef = NULL; expect_error(CSLUL_E_VERSIONWITHOUTAPIDEF, 1, 1); TEST_SOURCE("since 1.6\n" "data int i = 123\n", 1); tsoftassert(ctx->typedepth == -1); free_ctx(ctx); } static void test_parse_toplevel_data_versioninimpl(void) { struct CSlul *ctx = create_ctx(CSLUL_P_IFACE); tassert(cslul_ll_start_phase(ctx, CSLUL_P_IMPL)); expect_error(CSLUL_E_SINCEVERSIONINIMPL, 1, 1); TEST_SOURCE("since 1.6\n" "data int i = 123\n", 1); tsoftassert(ctx->typedepth == -1); free_ctx(ctx); } static void test_parse_toplevel_data_nosuchversion(void) { struct CSlul *ctx = create_ctx(CSLUL_P_IFACE); ctx->parsed_module->num_apidefs = 0; expect_error(CSLUL_E_NOSUCHVERSION, 1, 7); TEST_SOURCE("since 1.6\n" "data int i\n", 1); tsoftassert(ctx->typedepth == -1); free_ctx(ctx); } static void test_parse_toplevel_data_sincecurly(void) { struct CSlul *ctx = create_ctx(CSLUL_P_IFACE); ctx->parsed_module->num_apidefs = 0; expect_error(CSLUL_E_EXPECTEDVERORRCURLY, 1, 12); TEST_SOURCE("since { 1.2\n" "data int i\n", 1); tsoftassert(ctx->typedepth == -1); free_ctx(ctx); } static void test_parse_toplevel_bad_start(void) { struct CSlul *ctx = create_ctx(CSLUL_P_IMPL); expect_error(CSLUL_E_BADTOPLEVELTOKEN, 1, 1); /* Should skip until the next toplevel, i.e. until the end in this case */ TEST_SOURCE("int i\njunk\n func\n", 1); tsoftassert(ctx->line == 4); tsoftassert(ctx->startcolumn == 1); free_ctx(ctx); } static void test_parse_toplevel_bad_column(void) { struct CSlul *ctx = create_ctx(CSLUL_P_IMPL); expect_error(CSLUL_E_BADTOPLEVELCOLUMN, 1, 2); TEST_SOURCE(" data int i\n", 1); free_ctx(ctx); } static void test_parse_toplevel_type(void) { struct CSlul *ctx = create_ctx(CSLUL_P_IMPL); struct TypeDecl *decl; TEST_SOURCE("type I = int\n", 1); tsoftassert(!ctx->tl.idents_root); tassert(ctx->tl.types_root); ASSERT_LINECOL(ctx->tl.types_root, 1, 6); tsoftassert(!ctx->tl.types_root->lower); tsoftassert(!ctx->tl.types_root->higher); tsoftassert(ctx->tl.types_root->length == 1); tsoftassert(ctx->tl.types_root->name.copy[0] == 'I'); decl = (struct TypeDecl*)ctx->tl.types_root; ASSERT_TYPE_INT(&decl->type); free_ctx(ctx); } static void test_parse_toplevel_type_dupl(void) { struct CSlul *ctx = create_ctx(CSLUL_P_IMPL); expect_error(CSLUL_E_IDENTEXISTS, 2, 6); TEST_SOURCE("type I = int\ntype I = int\n", 1); tsoftassert(ctx->typedepth == -1); free_ctx(ctx); } static void test_parse_toplevel_type_noident(void) { struct CSlul *ctx = create_ctx(CSLUL_P_IMPL); expect_error(CSLUL_E_IDENTIFIEREXPECTED, 1, 6); TEST_SOURCE("type = int\n", 1); tsoftassert(ctx->typedepth == -1); free_ctx(ctx); } static void test_parse_toplevel_type_noequals(void) { struct CSlul *ctx = create_ctx(CSLUL_P_IMPL); expect_error(CSLUL_E_TYPEDEFWITHOUTEQUAL, 1, 7); TEST_SOURCE("type I int\n", 1); tsoftassert(ctx->typedepth == -1); free_ctx(ctx); } static void test_parse_toplevel_type_badtype(void) { struct CSlul *ctx = create_ctx(CSLUL_P_IMPL); expect_error(CSLUL_E_BADTYPE, 1, 10); TEST_SOURCE("type I = if\n", 1); tsoftassert(ctx->typedepth == -1); free_ctx(ctx); } static void test_parse_toplevel_type_badqual(void) { struct CSlul *ctx = create_ctx(CSLUL_P_IMPL); expect_error(CSLUL_E_QUALNOTALLOWED, 1, 10); TEST_SOURCE("type T = var int\n", 1); tsoftassert(ctx->typedepth == -1); free_ctx(ctx); } static void test_parse_type_ref(void) { struct CSlul *ctx = create_ctx(CSLUL_P_IMPL); struct TypeDecl *t, *u, *v; TEST_SOURCE("type T = ref int\n" "type U = arena byte\n" "type V = own int\n", 1); t = lookup_type(ctx, "T"); u = lookup_type(ctx, "U"); v = lookup_type(ctx, "V"); tassert(t && u && v); ASSERT_TYPE_REF(&t->type, IMPLQUALS, M_NORMAL); ASSERT_TYPE_INT(t->type.u.nested); ASSERT_LINECOL(&t->ident, 1, 6); ASSERT_TYPE_REF(&u->type, IMPLQUALS, M_ARENA); ASSERT_TYPE_BYTE(u->type.u.nested); ASSERT_LINECOL(&u->ident, 2, 6); ASSERT_TYPE_REF(&v->type, IMPLQUALS, M_OWN); ASSERT_TYPE_INT(v->type.u.nested); ASSERT_LINECOL(&v->ident, 3, 6); tsoftassert(ctx->typedepth == -1); free_ctx(ctx); } static void test_parse_type_ref_optional(void) { struct CSlul *ctx = create_ctx(CSLUL_P_IMPL); struct TypeDecl *t, *u, *v; TEST_SOURCE("type T = ?ref int\n" "type U = ?arena byte\n" "type V = ?own int\n", 1); t = lookup_type(ctx, "T"); u = lookup_type(ctx, "U"); v = lookup_type(ctx, "V"); tassert(t && u && v); ASSERT_TYPE_REF(&t->type, IMPLQUALS, M_OPTIONAL|M_NORMAL); ASSERT_TYPE_INT(t->type.u.nested); ASSERT_LINECOL(&t->ident, 1, 6); ASSERT_TYPE_REF(&u->type, IMPLQUALS, M_OPTIONAL|M_ARENA); ASSERT_TYPE_BYTE(u->type.u.nested); ASSERT_LINECOL(&u->ident, 2, 6); ASSERT_TYPE_REF(&v->type, IMPLQUALS, M_OPTIONAL|M_OWN); ASSERT_TYPE_INT(v->type.u.nested); ASSERT_LINECOL(&v->ident, 3, 6); tsoftassert(ctx->typedepth == -1); free_ctx(ctx); } static void test_parse_type_optional_bad(void) { struct CSlul *ctx = create_ctx(CSLUL_P_IMPL); expect_error(CSLUL_E_OPTIONALNONREF, 4, 19); expect_error(CSLUL_E_OPTIONALNONREF, 3, 15); expect_error(CSLUL_E_OPTIONALNONREF, 2, 11); expect_error(CSLUL_E_OPTIONALNONREF, 1, 11); TEST_SOURCE("type T = ?int\n" "type U = ?var byte\n" "type V = ref ?[3]int\n" "type W = ref var ?[3]int\n", 1); free_ctx(ctx); } static void test_parse_toplevel_type_eof1(void) { struct CSlul *ctx = create_ctx(CSLUL_P_IMPL); expect_error(CSLUL_E_UNEXPECTEDEOF, 1, 9); TEST_SOURCE("type T =\n", 1); /* ctx->typedepth is not decremented on EOF */ free_ctx(ctx); } static void test_parse_toplevel_type_eof2(void) { struct CSlul *ctx = create_ctx(CSLUL_P_IFACE); expect_error(CSLUL_E_UNEXPECTEDEOF, 2, 16); TEST_SOURCE("since 1.0\n" "type T = closed\n", 1); /* ctx->typedepth is not decremented on EOF */ free_ctx(ctx); } static void test_parse_type_array_largelength(void) { struct CSlul *ctx = create_ctx(CSLUL_P_IMPL); struct TypeDecl *decl; TEST_SOURCE("type T = [18446744073709551615]int\n", 1); decl = (struct TypeDecl*)ctx->tl.types_root; tassert(decl); ASSERT_TYPE_ARRAY(&decl->type, UINT64_MAX); ASSERT_TYPE_INT(&decl->type.u.arr->elemtype); tsoftassert(ctx->typedepth == -1); free_ctx(ctx); } static void test_parse_type_array_noexpr(void) { struct CSlul *ctx = create_ctx(CSLUL_P_IMPL); expect_error(CSLUL_E_INCOMPLETEEXPR, 1, 12); TEST_SOURCE("type A1 = []\n", 0); tsoftassert(ctx->typedepth == -1); expect_error(CSLUL_E_UNEXPECTEDEOF, 2, 12); TEST_SOURCE("type A2 = [\n", 1); free_ctx(ctx); } static void test_parse_type_array_noendbracket(void) { struct CSlul *ctx = create_ctx(CSLUL_P_IMPL); expect_error(CSLUL_E_BADEXPRTOKEN, 1, 16); TEST_SOURCE("type A1 = [123 int\n", 1); tsoftassert(ctx->typedepth == -1); expect_error(CSLUL_E_UNEXPECTEDEOF, 2, 15); TEST_SOURCE("type A2 = [123\n", 1); free_ctx(ctx); } static void test_parse_type_array_noelemtype(void) { struct CSlul *ctx = create_ctx(CSLUL_P_IMPL); expect_error(CSLUL_E_BADTYPE, 1, 16); TEST_SOURCE("type A1 = [123]return\n", 0); tsoftassert(ctx->typedepth == -1); expect_error(CSLUL_E_UNEXPECTEDEOF, 2, 16); TEST_SOURCE("type A2 = [123]\n", 1); free_ctx(ctx); } /** Test of definition of multi-dimensional array type */ static void test_parse_type_array_multidim(void) { struct CSlul *ctx = create_ctx(CSLUL_P_IMPL); struct TypeDecl *decl; struct Type *type; TEST_SOURCE("type T = [2,3,4]byte\n", 1); decl = (struct TypeDecl*)ctx->tl.types_root; tassert(decl); type = &decl->type; ASSERT_TYPE_ARRAY(type, 2); type = &type->u.arr->elemtype; ASSERT_TYPE_ARRAY(type, 3); type = &type->u.arr->elemtype; ASSERT_TYPE_ARRAY(type, 4); type = &type->u.arr->elemtype; ASSERT_TYPE_BYTE(type); tsoftassert(ctx->typedepth == -1); free_ctx(ctx); } static void test_parse_type_array_multidim_empty1(void) { struct CSlul *ctx = create_ctx(CSLUL_P_IMPL); expect_error(CSLUL_E_INCOMPLETEEXPR, 1, 13); TEST_SOURCE("type A = [2,]int\n", 1); tsoftassert(ctx->typedepth == -1); free_ctx(ctx); } static void test_parse_type_array_multidim_empty2(void) { struct CSlul *ctx = create_ctx(CSLUL_P_IMPL); expect_error(CSLUL_E_INCOMPLETEEXPR, 1, 11); TEST_SOURCE("type A = [,2]int\n", 1); tsoftassert(ctx->typedepth == -1); free_ctx(ctx); } static void test_parse_type_array_multidim_empty3(void) { struct CSlul *ctx = create_ctx(CSLUL_P_IMPL); expect_error(CSLUL_E_INCOMPLETEEXPR, 1, 13); TEST_SOURCE("type A = [2,,3]int\n", 1); tsoftassert(ctx->typedepth == -1); free_ctx(ctx); } static void test_parse_type_ident(void) { struct CSlul *ctx = create_ctx(CSLUL_P_IMPL); struct TypeDecl *t, *u, *v; TEST_SOURCE("type T = int\ntype U = T\ntype V = ref U\n", 1); t = lookup_type(ctx, "T"); u = lookup_type(ctx, "U"); v = lookup_type(ctx, "V"); tassert(t && u && v); ASSERT_TYPE_INT(&t->type); ASSERT_TYPE_IDENT(&u->type, IMPLQUALS, t); ASSERT_TYPE_REF(&v->type, IMPLQUALS, M_NORMAL); ASSERT_TYPE_IDENT(v->type.u.nested, IMPLQUALS, u); tsoftassert(ctx->typedepth == -1); free_ctx(ctx); } static void test_parse_type_private(void) { struct CSlul *ctx = create_ctx(CSLUL_P_IFACE); struct TypeDecl *t; TEST_SOURCE("since 1.0\n" "type T\n", 1); t = lookup_type_ver(ctx, "T", "1.0"); tassert(t != NULL); ASSERT_TYPE_PRIVATE(&t->type); tsoftassert(ctx->typedepth == -1); free_ctx(ctx); } static void test_parse_type_private_notiface1(void) { struct CSlul *ctx = create_ctx(CSLUL_P_IMPL); expect_error(CSLUL_E_PRIVATEINIMPL, 1, 6); TEST_SOURCE("type T\n", 1); tsoftassert(ctx->typedepth == -1); free_ctx(ctx); } static void test_parse_type_private_notiface2(void) { struct CSlul *ctx = create_ctx(CSLUL_P_IMPL); expect_error(CSLUL_E_PRIVATEINIMPL, 1, 6); TEST_SOURCE("type T1\n" "type T2 = int\n", 1); tsoftassert(ctx->typedepth == -1); free_ctx(ctx); } static void test_parse_type_struct(void) { struct CSlul *ctx = create_ctx(CSLUL_P_IMPL); struct TypeDecl *t, *u, *v; struct IdentDecl *x, *y, *b; TEST_SOURCE( "type T = struct {}\n" "type U = struct {\n" " int x\n" " byte b\n" "}\n" "type V = struct {\n" " int y\n" " bool b\n" "}\n", 1); /* T */ t = lookup_type(ctx, "T"); tassert(t != NULL); tsoftassert(t->type.type == T_STRUCT); tsoftassert(t->type.quals == IMPLQUALS); ASSERT_LINECOL(&t->ident, 1, 6); tassert(t->type.u.fields != NULL); tsoftassert(t->type.u.fields->first == NULL); tsoftassert(t->type.u.fields->fields_root == NULL); tsoftassert(t->type.u.fields->count == 0); t = NULL; /* U */ u = lookup_type(ctx, "U"); tassert(u != NULL); tsoftassert(u->type.type == T_STRUCT); tsoftassert(u->type.quals == IMPLQUALS); ASSERT_LINECOL(&u->ident, 2, 6); tassert(u->type.u.fields != NULL); tassert(u->type.u.fields->first != NULL); tassert(u->type.u.fields->first->next != NULL); tsoftassert(u->type.u.fields->first->next->next == NULL); tsoftassert(u->type.u.fields->fields_root != NULL); tsoftassert(u->type.u.fields->count == 2); x = (struct IdentDecl*)lookup(ctx, u->type.u.fields->fields_root, "x"); b = (struct IdentDecl*)lookup(ctx, u->type.u.fields->fields_root, "b"); tassert(x && b); ASSERT_TYPE_INT(&x->type); ASSERT_LINECOL(&x->ident, 3, 9); tsoftassert(x->u.initval == NULL); ASSERT_TYPE_BYTE(&b->type); ASSERT_LINECOL(&b->ident, 4, 10); tsoftassert(b->u.initval == NULL); u = NULL; x = b = NULL; /* V */ v = lookup_type(ctx, "V"); tassert(v != NULL); tsoftassert(v->type.type == T_STRUCT); tsoftassert(v->type.quals == IMPLQUALS); ASSERT_LINECOL(&v->ident, 6, 6); tassert(v->type.u.fields != NULL); tassert(v->type.u.fields->first != NULL); tassert(v->type.u.fields->first->next != NULL); tsoftassert(v->type.u.fields->first->next->next == NULL); tsoftassert(v->type.u.fields->fields_root != NULL); tsoftassert(v->type.u.fields->count == 2); y = (struct IdentDecl*)lookup(ctx, v->type.u.fields->fields_root, "y"); b = (struct IdentDecl*)lookup(ctx, v->type.u.fields->fields_root, "b"); tassert(y && b); ASSERT_TYPE_INT(&y->type); ASSERT_LINECOL(&y->ident, 7, 9); tsoftassert(y->u.initval == NULL); ASSERT_TYPE_BOOL(&b->type); ASSERT_LINECOL(&b->ident, 8, 10); tsoftassert(b->u.initval == NULL); tsoftassert(ctx->typedepth == -1); /* Ensure that the struct fields don't become visible in the toplevel namespace */ tsoftassert(lookup_toplevel(ctx, "x") == NULL); tsoftassert(lookup_toplevel(ctx, "y") == NULL); tsoftassert(lookup_toplevel(ctx, "b") == NULL); free_ctx(ctx); } static void test_parse_type_enum(void) { struct CSlul *ctx = create_ctx(CSLUL_P_IFACE); struct TypeDecl *t, *u, *v; struct EnumValue *a, *b; TEST_SOURCE( "since 1.0\n" "type T = enum {}\n" "since 1.0\n" "type U = enum byte {\n" " a = 254\n" " b\n" "}\n" "since 1.0\n" "type V = closed enum {\n" " a\n" " b\n" "}\n", 1); /* T */ t = lookup_type_ver(ctx, "T", "1.0"); tassert(t != NULL); tassert(t->type.type == T_ENUM); tsoftassert(t->type.quals == 0); tassert(t->type.u.enu != NULL); ASSERT_TYPE_INT(&t->type.u.enu->base); tsoftassert(t->type.u.enu->values == NULL); tsoftassert(t->type.u.enu->values_root == NULL); t = NULL; /* U */ u = lookup_type_ver(ctx, "U", "1.0"); tassert(u != NULL); tassert(u->type.type == T_ENUM); tsoftassert(u->type.quals == 0); tassert(u->type.u.enu != NULL); ASSERT_TYPE_ELEMENTARY(&u->type.u.enu->base, BT_Byte); tassert(u->type.u.enu->values != NULL); tsoftassert(u->type.u.enu->values_root != NULL); a = (struct EnumValue *)lookup(ctx, u->type.u.enu->values_root, "a"); b = (struct EnumValue *)lookup(ctx, u->type.u.enu->values_root, "b"); tassert(a && b); tsoftassert(u->type.u.enu->values != NULL); tsoftassert(&u->type.u.enu->values->e == a); tsoftassert(u->type.u.enu->values->next != NULL); tsoftassert(&u->type.u.enu->values->next->e == b); ASSERT_TYPE_ELEMENTARY(&a->vd.decl.type, BT_Byte); ASSERT_LINECOL(&a->vd.decl.ident, 5, 5); ASSERT_EXPR_INT(a->vd.decl.u.initval, 254); tsoftassert(a->vd.sinceversions == NULL); ASSERT_TYPE_ELEMENTARY(&b->vd.decl.type, BT_Byte); ASSERT_LINECOL(&b->vd.decl.ident, 6, 5); tsoftassert(b->vd.decl.u.initval == NULL); tsoftassert(b->vd.sinceversions == NULL); u = NULL; /* V */ v = lookup_type_ver(ctx, "V", "1.0"); tassert(v != NULL); tassert(v->type.type == T_ENUM); tsoftassert(v->type.quals == Q_CLOSED); tassert(v->type.u.enu != NULL); tsoftassert(v->type.u.enu->base.type == 0); /* inferred after parsing */ tassert(v->type.u.enu->values != NULL); tsoftassert(v->type.u.enu->values_root != NULL); a = (struct EnumValue *)lookup(ctx, v->type.u.enu->values_root, "a"); b = (struct EnumValue *)lookup(ctx, v->type.u.enu->values_root, "b"); tassert(a && b); tsoftassert(v->type.u.enu->values != NULL); tsoftassert(&v->type.u.enu->values->e == a); tsoftassert(v->type.u.enu->values->next != NULL); tsoftassert(&v->type.u.enu->values->next->e == b); ASSERT_LINECOL(&a->vd.decl.ident, 10, 5); tsoftassert(a->vd.decl.u.initval == NULL); tsoftassert(a->vd.sinceversions == NULL); ASSERT_LINECOL(&b->vd.decl.ident, 11, 5); tsoftassert(b->vd.decl.u.initval == NULL); tsoftassert(b->vd.sinceversions == NULL); tsoftassert(ctx->typedepth == -1); /* Ensure that the enum values don't become visible in the toplevel namespace */ tsoftassert(lookup_toplevel(ctx, "a") == NULL); tsoftassert(lookup_toplevel(ctx, "b") == NULL); free_ctx(ctx); } static void test_parse_type_enum_versioned(void) { struct CSlul *ctx = create_ctx(CSLUL_P_IFACE); struct TypeDecl *t, *u; struct EnumValue *a, *b; struct ApiDef *ver12, *ver13; expect_error(CSLUL_E_MISSINGVERWITHAPIDEF, 1, 1); TEST_SOURCE( "type T = enum byte {\n" /* missing version */ " a\n" " since 1.3\n" " b\n" "}\n" "since 1.1\n" "type U = enum int {\n" /* correct definition with version */ " a = 255\n" " since 1.2\n" " b = 32767\n" "}\n", 1); /* Versions */ ver12 = (struct ApiDef *)lookup(ctx, ctx->module.apidefs_root, "1.2"); ver13 = (struct ApiDef *)lookup(ctx, ctx->module.apidefs_root, "1.3"); /* T */ t = lookup_type(ctx, "T"); tassert(t != NULL); tassert(t->type.type == T_ENUM); tsoftassert(t->type.quals == 0); tassert(t->type.u.enu != NULL); ASSERT_TYPE_ELEMENTARY(&t->type.u.enu->base, BT_Byte); tassert(t->type.u.enu->values != NULL); tsoftassert(t->type.u.enu->values_root != NULL); a = (struct EnumValue *)lookup(ctx, t->type.u.enu->values_root, "a"); b = (struct EnumValue *)lookup(ctx, t->type.u.enu->values_root, "b"); tassert(a && b); /* T.a */ tsoftassert(t->type.u.enu->values != NULL); tsoftassert(&t->type.u.enu->values->e == a); tsoftassert(t->type.u.enu->values->next != NULL); tsoftassert(&t->type.u.enu->values->next->e == b); ASSERT_TYPE_ELEMENTARY(&a->vd.decl.type, BT_Byte); ASSERT_LINECOL(&a->vd.decl.ident, 2, 5); tsoftassert(a->vd.decl.u.initval == NULL); tsoftassert(a->vd.sinceversions == NULL); /* T.b since 1.3 */ ASSERT_TYPE_ELEMENTARY(&b->vd.decl.type, BT_Byte); ASSERT_LINECOL(&b->vd.decl.ident, 4, 5); tsoftassert(b->vd.decl.u.initval == NULL); tassert(b->vd.sinceversions != NULL); tsoftassert(b->vd.sinceversions->next == NULL); tsoftassert(b->vd.sinceversions->version == ver13); t = NULL; /* U since 1.1 */ u = lookup_type_ver(ctx, "U", "1.1"); tassert(u != NULL); tassert(u->type.type == T_ENUM); tsoftassert(u->type.quals == 0); tassert(u->type.u.enu != NULL); ASSERT_TYPE_ELEMENTARY(&u->type.u.enu->base, BT_Int); /* non-closed enums default to int type */ tassert(u->type.u.enu->values != NULL); tsoftassert(u->type.u.enu->values_root != NULL); a = (struct EnumValue *)lookup(ctx, u->type.u.enu->values_root, "a"); b = (struct EnumValue *)lookup(ctx, u->type.u.enu->values_root, "b"); tassert(a && b); tsoftassert(u->type.u.enu->values != NULL); tsoftassert(&u->type.u.enu->values->e == a); tsoftassert(u->type.u.enu->values->next != NULL); tsoftassert(&u->type.u.enu->values->next->e == b); /* U.a */ ASSERT_LINECOL(&a->vd.decl.ident, 8, 5); ASSERT_EXPR_INT(a->vd.decl.u.initval, 255); tsoftassert(a->vd.sinceversions == NULL); /* U.b since 1.2 */ ASSERT_TYPE_ELEMENTARY(&b->vd.decl.type, BT_Int); ASSERT_EXPR_INT(b->vd.decl.u.initval, 32767); ASSERT_LINECOL(&b->vd.decl.ident, 10, 5); tassert(b->vd.sinceversions != NULL); tsoftassert(b->vd.sinceversions->next == NULL); tsoftassert(b->vd.sinceversions->version == ver12); tsoftassert(ctx->typedepth == -1); free_ctx(ctx); } static void test_parse_type_struct_nested(void) { struct CSlul *ctx = create_ctx(CSLUL_P_IMPL); struct TypeDecl *t; struct IdentDecl *x, *y, *b; TEST_SOURCE( "type T = struct {\n" " int x\n" " struct {\n" " bool b\n" " } y\n" " byte b\n" "}\n", 1); t = lookup_type(ctx, "T"); tassert(t != NULL); tsoftassert(t->type.type == T_STRUCT); tsoftassert(t->type.quals == IMPLQUALS); ASSERT_LINECOL(&t->ident, 1, 6); tassert(t->type.u.fields != NULL); tassert(t->type.u.fields->first != NULL); tassert(t->type.u.fields->first->next != NULL); tassert(t->type.u.fields->first->next->next != NULL); tsoftassert(t->type.u.fields->first->next->next->next == NULL); tsoftassert(t->type.u.fields->fields_root != NULL); tsoftassert(t->type.u.fields->count == 3); x = (struct IdentDecl*)lookup(ctx, t->type.u.fields->fields_root, "x"); y = (struct IdentDecl*)lookup(ctx, t->type.u.fields->fields_root, "y"); b = (struct IdentDecl*)lookup(ctx, t->type.u.fields->fields_root, "b"); tassert(x && y && b); ASSERT_TYPE_INT(&x->type); ASSERT_LINECOL(&x->ident, 2, 9); tsoftassert(x->u.initval == NULL); ASSERT_TYPE_BYTE(&b->type); ASSERT_LINECOL(&b->ident, 6, 10); /* of the "byte b" at the end */ tsoftassert(b->u.initval == NULL); /* Inspect the nested struct */ tsoftassert(y->type.type == T_STRUCT); tsoftassert(y->type.quals == IMPLQUALS); tsoftassert(y->type.misc == M_KNOWN_SIZE); ASSERT_LINECOL(&y->ident, 5, 7); tassert(y->type.u.fields != NULL); tassert(y->type.u.fields->first != NULL); tsoftassert(y->type.u.fields->first->next == NULL); tsoftassert(y->type.u.fields->fields_root != NULL); tsoftassert(y->type.u.fields->count == 1); tsoftassert(y->u.initval == NULL); b = (struct IdentDecl*)lookup(ctx, y->type.u.fields->fields_root, "b"); ASSERT_TYPE_BOOL(&b->type); ASSERT_LINECOL(&b->ident, 4, 14); /* of the nested "bool b" */ tsoftassert(b->u.initval == NULL); tsoftassert(ctx->typedepth == -1); free_ctx(ctx); } static void test_parse_type_struct_versioned(void) { struct CSlul *ctx = create_ctx(CSLUL_P_IFACE); struct TypeDecl *t; struct FieldOrParam *x, *y, *b; struct ApiDef *ver11, *ver12; TEST_SOURCE( "since 1.0\n" "type T = struct {\n" " int x\n" " since 1.1\n" " struct {\n" " bool b\n" " } y\n" " since 1.2\n" " byte b\n" "}\n", 1); ver11 = (struct ApiDef *)lookup(ctx, ctx->module.apidefs_root, "1.1"); ver12 = (struct ApiDef *)lookup(ctx, ctx->module.apidefs_root, "1.2"); t = lookup_type_ver(ctx, "T", "1.0"); tassert(t != NULL); tsoftassert(t->type.type == T_STRUCT); tsoftassert(t->type.quals == 0); ASSERT_LINECOL(&t->ident, 2, 6); tassert(t->type.u.fields != NULL); tassert(t->type.u.fields->first != NULL); tassert(t->type.u.fields->first->next != NULL); tassert(t->type.u.fields->first->next->next != NULL); tsoftassert(t->type.u.fields->first->next->next->next == NULL); tsoftassert(t->type.u.fields->fields_root != NULL); tsoftassert(t->type.u.fields->count == 3); x = (struct FieldOrParam *)lookup(ctx, t->type.u.fields->fields_root, "x"); y = (struct FieldOrParam *)lookup(ctx, t->type.u.fields->fields_root, "y"); b = (struct FieldOrParam *)lookup(ctx, t->type.u.fields->fields_root, "b"); tassert(x && y && b); ASSERT_TYPE_INT(&x->vardef.decl.type); ASSERT_LINECOL(&x->vardef.decl.ident, 3, 9); tsoftassert(x->vardef.decl.u.initval == NULL); tsoftassert(x->vardef.var_id == 0); tsoftassert(x->sinceversions == NULL); ASSERT_TYPE_BYTE(&b->vardef.decl.type); ASSERT_LINECOL(&b->vardef.decl.ident, 9, 10); tsoftassert(b->vardef.decl.u.initval == NULL); tsoftassert(b->vardef.var_id == 2); tassert(b->sinceversions != NULL); tsoftassert(b->sinceversions->next == NULL); tsoftassert(b->sinceversions->version == ver12); /* Inspect the nested struct */ tsoftassert(y->vardef.decl.type.type == T_STRUCT); tsoftassert(y->vardef.decl.type.quals == Q_CLOSED); ASSERT_LINECOL(&y->vardef.decl.ident, 7, 7); tassert(y->vardef.decl.type.u.fields != NULL); tassert(y->vardef.decl.type.u.fields->first != NULL); tsoftassert(y->vardef.decl.type.u.fields->first->next == NULL); tsoftassert(y->vardef.decl.type.u.fields->fields_root != NULL); tsoftassert(y->vardef.decl.type.u.fields->count == 1); tsoftassert(y->vardef.decl.u.initval == NULL); tsoftassert(y->vardef.var_id == 1); tassert(y->sinceversions != NULL); tsoftassert(y->sinceversions->next == NULL); tsoftassert(y->sinceversions->version == ver11); /* Nested "bool b" */ b = (struct FieldOrParam *)lookup(ctx, y->vardef.decl.type.u.fields->fields_root, "b"); ASSERT_TYPE_BOOL(&b->vardef.decl.type); ASSERT_LINECOL(&b->vardef.decl.ident, 6, 14); tsoftassert(b->vardef.decl.u.initval == NULL); tsoftassert(b->vardef.var_id == 0); tsoftassert(b->sinceversions == NULL); tsoftassert(ctx->typedepth == -1); free_ctx(ctx); } static void test_parse_type_struct_duplfield(void) { struct CSlul *ctx = create_ctx(CSLUL_P_IMPL); expect_error(CSLUL_E_DUPLFIELD, 3, 9); TEST_SOURCE( "type Y = struct {\n" " int x\n" " int x\n" "}\n", 1); tsoftassert(ctx->typedepth == -1); free_ctx(ctx); } static void test_parse_type_struct_badsemicolon(void) { struct CSlul *ctx = create_ctx(CSLUL_P_IMPL); expect_error(CSLUL_E_BADSEMICOLON, 2, 10); TEST_SOURCE( "type Y = struct {\n" " int x;\n" " int y\n" "}\n", 1); tsoftassert(ctx->typedepth == -1); free_ctx(ctx); } static void test_parse_type_enum_duplident(void) { struct CSlul *ctx = create_ctx(CSLUL_P_IMPL); expect_error(CSLUL_E_DUPLENUMIDENT, 3, 5); TEST_SOURCE( "type E = enum {\n" " a\n" " a\n" "}\n", 1); tsoftassert(ctx->typedepth == -1); free_ctx(ctx); } static void test_parse_type_enum_wrongorder(void) { struct CSlul *ctx = create_ctx(CSLUL_P_IFACE); expect_error(CSLUL_E_UNVERSIONEDAFTERVERSIONED, 5, 5); TEST_SOURCE( "since 1.0\n" "type E = enum {\n" " since 1.2\n" " a\n" " b\n" "}\n", 1); tsoftassert(ctx->typedepth == -1); free_ctx(ctx); } static void test_parse_type_enum_closed_version(void) { struct CSlul *ctx = create_ctx(CSLUL_P_IFACE); expect_error(CSLUL_E_VERSIONEDCLOSEDTYPE, 4, 5); TEST_SOURCE( "since 1.0\n" "type E = closed enum {\n" " a\n" " since 1.2\n" " b\n" "}\n", 1); tsoftassert(ctx->typedepth == -1); free_ctx(ctx); } static void test_parse_type_enum_badcomma(void) { struct CSlul *ctx = create_ctx(CSLUL_P_IMPL); expect_error(CSLUL_E_ENUMCOMMA, 2, 6); TEST_SOURCE( "type E = enum {\n" " a,\n" " b\n" "}\n", 1); tsoftassert(ctx->typedepth == -1); free_ctx(ctx); } static void test_parse_type_enum_badquals(void) { struct CSlul *ctx = create_ctx(CSLUL_P_IFACE); expect_error(CSLUL_E_QUALNOTALLOWED, 2, 15); TEST_SOURCE("since 1.0\n" "type E = enum var int {\n" " a\n" "}\n", 1); tsoftassert(ctx->typedepth == -1); free_ctx(ctx); } static void test_parse_type_func_noargs(void) { struct CSlul *ctx = create_ctx(CSLUL_P_IMPL); struct TypeDecl *t; TEST_SOURCE("type T = funcref ()\n", 1); t = lookup_type(ctx, "T"); tassert(t != NULL); tsoftassert(t->type.type == T_FUNC); tsoftassert(t->type.quals == IMPLQUALS); ASSERT_LINECOL(&t->ident, 1, 6); tsoftassert(t->type.misc == M_VOIDRETURN); tassert(t->type.u.func != NULL); tsoftassert(t->type.u.func->returntype.type == T_INTERNAL); tsoftassert(t->type.u.func->returntype.u.internal == IT_Void); tsoftassert(t->type.u.func->params.first == NULL); tsoftassert(t->type.u.func->params.fields_root == NULL); tsoftassert(t->type.u.func->params.count == 0); tsoftassert(ctx->typedepth == -1); free_ctx(ctx); } static void test_parse_type_quals(void) { struct CSlul *ctx = create_ctx(CSLUL_P_IFACE); struct TypeDecl *t, *u; TEST_SOURCE( "since 1.0\n" "type T = struct {}\n" "since 1.0\n" "type U = closed struct {}\n", 1); /* T */ t = lookup_type_ver(ctx, "T", "1.0"); tassert(t != NULL); tsoftassert(t->type.type == T_STRUCT); tsoftassert(t->type.quals == 0); tassert(t->type.u.fields != NULL); tsoftassert(t->type.u.fields->first == NULL); tsoftassert(t->type.u.fields->fields_root == NULL); tsoftassert(t->type.u.fields->count == 0); /* U */ u = lookup_type_ver(ctx, "U", "1.0"); tassert(u != NULL); tsoftassert(u->type.type == T_STRUCT); tsoftassert(u->type.quals == Q_CLOSED); tassert(u->type.u.fields != NULL); tsoftassert(u->type.u.fields->first == NULL); tsoftassert(u->type.u.fields->fields_root == NULL); tsoftassert(u->type.u.fields->count == 0); tsoftassert(ctx->typedepth == -1); free_ctx(ctx); } static void test_parse_type_quals_duplicated(void) { struct CSlul *ctx = create_ctx(CSLUL_P_IFACE); expect_error(CSLUL_E_DUPLQUAL, 2, 17); TEST_SOURCE("since 1.0\n" "type T = closed closed struct {}\n", 1); tsoftassert(ctx->typedepth == -1); free_ctx(ctx); } static void test_parse_type_quals_implclosed(void) { struct CSlul *ctx = create_ctx(CSLUL_P_IMPL); expect_error(CSLUL_E_BADIMPLQUAL, 1, 10); TEST_SOURCE("type T = closed struct {}\n", 1); tsoftassert(ctx->typedepth == -1); free_ctx(ctx); } static void test_parse_type_generic_use(void) { struct CSlul *ctx = create_ctx(CSLUL_P_IFACE); const struct IdentDecl *l; const struct Type *t; const struct GenericPrm *gprm; /* refs are actually not allowed at top level, but it simplifies the test */ expect_error(CSLUL_E_BADTLDATAREF, 2, 6); TEST_SOURCE("since 1.0\n" "data ref List l\n", 1); l = lookup_toplevel_ver(ctx, "l", "1.0"); tassert(l != NULL); ASSERT_LINECOL(&l->ident, 2, 26); tsoftassert(l->u.initval == NULL); /* ref Xxx */ tassert(l->type.type == T_REF); tsoftassert(l->type.defflags == D_DEFINED); tsoftassert(l->type.quals == 0); tsoftassert(l->type.misc == M_NORMAL); t = l->type.u.nested; /* Xxx */ tassert(t->type == T_GENERICSPEC); tsoftassert(t->quals == 0); tsoftassert(t->misc == 1); /* number of parameters */ tassert((gprm = t->u.gprm) != NULL); /* List */ tassert(gprm->generictype != NULL); tassert(gprm->generictype->type == T_IDENT); tassert(gprm->generictype->quals == 0); /* ref Thing */ tassert(gprm->param.type.type == T_REF); tsoftassert(gprm->param.next == NULL); free_ctx(ctx); } static void test_parse_type_generic_use_2params(void) { struct CSlul *ctx = create_ctx(CSLUL_P_IFACE); const struct IdentDecl *l; const struct Type *t; const struct GenericPrm *gprm; const struct PrmEntry *prmentry; expect_error(CSLUL_E_BADTLDATAREF, 2, 6); TEST_SOURCE("since 1.0\n" "data ref TwoParams l\n", 1); l = lookup_toplevel_ver(ctx, "l", "1.0"); tassert(l != NULL); ASSERT_LINECOL(&l->ident, 2, 32); tsoftassert(l->u.initval == NULL); /* ref Xxx */ tassert(l->type.type == T_REF); tsoftassert(l->type.defflags == D_DEFINED); tsoftassert(l->type.quals == 0); tsoftassert(l->type.misc == M_NORMAL); t = l->type.u.nested; /* Xxx */ tassert(t->type == T_GENERICSPEC); tsoftassert(t->quals == 0); tsoftassert(t->misc == 2); /* number of parameters */ tassert((gprm = t->u.gprm) != NULL); /* TwoParams */ tassert(gprm->generictype != NULL); tassert(gprm->generictype->type == T_IDENT); tassert(gprm->generictype->quals == 0); /* int */ prmentry = &gprm->param; tassert(prmentry->type.type == T_ELMNTRY); tsoftassert(prmentry->type.u.builtin == BT_Int); tsoftassert((prmentry = prmentry->next) != NULL); /* ref A */ tassert(prmentry->type.type == T_REF); tassert(prmentry->type.misc == M_NORMAL); tsoftassert(prmentry->next == NULL); free_ctx(ctx); } static void test_parse_type_generic_use_noparams1(void) { struct CSlul *ctx = create_ctx(CSLUL_P_IMPL); expect_error(CSLUL_E_BADTYPE, 1, 15); expect_error(CSLUL_E_BADTLDATAREF, 1, 6); TEST_SOURCE("data ref List<> l\n", 1); tsoftassert(ctx->typedepth == -1); free_ctx(ctx); } static void test_parse_type_generic_use_noparams2(void) { struct CSlul *ctx = create_ctx(CSLUL_P_IFACE); ctx->parsed_module->is_unstable = 1; expect_error(CSLUL_E_BADTYPE, 1, 20); TEST_SOURCE("func sort(ref List<> l)\n", 1); tsoftassert(ctx->typedepth == -1); free_ctx(ctx); } static void test_parse_type_generic_use_trailingcomma(void) { struct CSlul *ctx = create_ctx(CSLUL_P_IFACE); ctx->parsed_module->is_unstable = 1; expect_error(CSLUL_E_BADTYPE, 1, 26); TEST_SOURCE("func sort(ref List l)\n", 1); tsoftassert(ctx->typedepth == -1); free_ctx(ctx); } static void test_parse_type_generic_use_missingend(void) { struct CSlul *ctx = create_ctx(CSLUL_P_IFACE); ctx->parsed_module->is_unstable = 1; TEST_SOURCE("func sort(ref Listgeneric_param_depth == 1); TEST_SOURCE(" ", 0); tsoftassert(ctx->generic_param_depth == 1); free_ctx(ctx); } static void test_parse_type_generic_use_toomany(void) { struct CSlul *ctx = create_ctx(CSLUL_P_IFACE); int i; TEST_SOURCE("since 1.0\n" "func sort(ref Thing t)\n", 1); tsoftassert(ctx->typedepth == -1); free_ctx(ctx); } static void test_parse_type_generic_define(void) { struct CSlul *ctx = create_ctx(CSLUL_P_IFACE); struct TypeDecl *t, *prmT, *prmU; struct Type *basetype; struct PrmDefEntry *prmentry; struct IdentDecl *x, *r; TEST_SOURCE( "since 1.0\n" "type Thing = struct {\n" " slot T x\n" "}\n" "since 1.0\n" "type OtherThing = struct {\n" " slot T e\n" " slot U r\n" "}\n", 1); /* Thing */ t = lookup_type_ver(ctx, "Thing", "1.0"); tassert(t != NULL); tassert(t->type.type == T_GENERICDEF); tsoftassert(t->type.quals == 0); tassert(t->type.u.gdef != NULL); tassert(t->type.u.gdef->params_root != NULL); prmentry = &t->type.u.gdef->paramdef; tassert(prmentry->prmtype == PT_ANY); ASSERT_IDENT_L(&prmentry->paramdecl.ident, 2, 12, "T"); prmT = &prmentry->paramdecl; tsoftassert(prmentry->next == NULL); basetype = &t->type.u.gdef->basetype; tassert(basetype->type == T_STRUCT); tassert(basetype->u.fields != NULL); tsoftassert(basetype->u.fields->count == 1); /* slot T x */ x = (struct IdentDecl*)lookup(ctx, basetype->u.fields->fields_root, "x"); tassert(x != NULL); tassert(x->type.type == T_SLOT); tsoftassert(x->type.quals == 0); tsoftassert(x->type.u.ident == prmT); /* OtherThing */ t = lookup_type_ver(ctx, "OtherThing", "1.0"); tassert(t != NULL); tassert(t->type.type == T_GENERICDEF); tsoftassert(t->type.quals == 0); tassert(t->type.u.gdef != NULL); tassert(t->type.u.gdef->params_root != NULL); prmentry = &t->type.u.gdef->paramdef; tassert(prmentry->prmtype == PT_ENUMTYPE); ASSERT_IDENT_L(&prmentry->paramdecl.ident, 6, 22, "T"); prmT = &prmentry->paramdecl; tassert(prmentry->next != NULL); prmentry = prmentry->next; tassert(prmentry->prmtype == PT_REFTYPE); ASSERT_IDENT_L(&prmentry->paramdecl.ident, 6, 29, "U"); prmU = &prmentry->paramdecl; tsoftassert(prmentry->next == NULL); basetype = &t->type.u.gdef->basetype; tassert(basetype->type == T_STRUCT); tassert(basetype->u.fields != NULL); tsoftassert(basetype->u.fields->count == 2); /* slot T e */ r = (struct IdentDecl*)lookup(ctx, basetype->u.fields->fields_root, "e"); tassert(r != NULL); tassert(r->type.type == T_SLOT); tsoftassert(r->type.quals == 0); tsoftassert(r->type.misc == 0); tsoftassert(r->type.u.ident == prmT); /* slot U r */ r = (struct IdentDecl*)lookup(ctx, basetype->u.fields->fields_root, "r"); tassert(r != NULL); tassert(r->type.type == T_SLOT); tsoftassert(r->type.quals == 0); tsoftassert(r->type.misc == 0); tsoftassert(r->type.u.ident == prmU); /* Ensure that the type parameters don't become visible in the toplevel namespace */ tsoftassert(lookup_type(ctx, "T") == NULL); tsoftassert(lookup_type(ctx, "U") == NULL); free_ctx(ctx); } static void test_parse_type_generic_define_toomany(void) { struct CSlul *ctx = create_ctx(CSLUL_P_IFACE); int i; TEST_SOURCE("since 1.0\n" "type Thing<", 0); for (i = 0; i < MAX_TYPE_PARAMS; i++) { char buff[9]; sprintf(buff, "ref T%02d,", i); sourceline = __LINE__; test_source(ctx, buff, 8, 0); } expect_error(CSLUL_E_TOOMANYTYPEPARAMS, 2, 812); TEST_SOURCE("ref T100> = struct {}\n", 1); tsoftassert(ctx->typedepth == -1); free_ctx(ctx); } static void test_parse_type_versioned(void) { struct CSlul *ctx = create_ctx(CSLUL_P_IFACE); struct TypeDecl *td; TEST_SOURCE("since 1.1\n" "type T = struct { int x }\n", 1); tsoftassert(ctx->typedepth == -1); td = lookup_type_ver(ctx, "T", "1.1"); tassert(td != NULL); tsoftassert(td->type.type == T_STRUCT); free_ctx(ctx); } static void test_parse_type_versioned_multiple(void) { struct CSlul *ctx = create_ctx(CSLUL_P_IFACE); struct TypeDecl *td; expect_error(CSLUL_E_IDENTEXISTS, 4, 6); TEST_SOURCE("since 1.1\n" "type T = struct { int x }\n" "since 1.2\n" "type T = enum { A B }\n", 1); tsoftassert(ctx->typedepth == -1); td = lookup_type_ver(ctx, "T", "1.1"); tassert(td != NULL); tsoftassert(td->type.type == T_STRUCT); free_ctx(ctx); } static void test_parse_type_versionref(void) { struct CSlul *ctx = create_ctx(CSLUL_P_IFACE); const struct TypeDecl *othertype, *td; const struct FieldOrParam *f; const struct Type *fidenttype; TEST_SOURCE("since 1.1\n" "type OtherType = struct { int x }\n" "since 1.2\n" "type TheType = struct {\n" " ref OtherType f\n" "}\n", 1); tsoftassert(ctx->typedepth == -1); /* OtherType */ othertype = lookup_type_ver(ctx, "OtherType", "1.1"); tassert(othertype != NULL); tassert(othertype->type.type == T_STRUCT); tassert(othertype->type.u.fields != NULL); tassert(othertype->type.u.fields->count == 1); /* TheType */ td = lookup_type_ver(ctx, "TheType", "1.2"); tassert(td != NULL); tassert(td->type.type == T_STRUCT); tassert(td->type.u.fields != NULL); tassert(td->type.u.fields->count == 1); /* Field in struct */ f = (struct FieldOrParam *)lookup(ctx, td->type.u.fields->fields_root, "f"); tassert(f != NULL); tassert(f->vardef.decl.type.type == T_REF); tsoftassert(f->vardef.decl.type.quals == 0); tsoftassert(f->vardef.decl.type.misc == M_NORMAL); tsoftassert(f->vardef.var_id == 0); tsoftassert(f->sinceversions == NULL); tassert((fidenttype = f->vardef.decl.type.u.nested) != NULL); tsoftassert(fidenttype->type == T_IDENT); tsoftassert(fidenttype->u.ident == othertype); free_ctx(ctx); } static void test_parse_type_badcase1(void) { struct CSlul *ctx = create_ctx(CSLUL_P_IFACE); struct TypeDecl *td; struct IdentDecl *dd; expect_error(CSLUL_E_LOWERCASETYPE, 2, 6); TEST_SOURCE("since 1.1\n" "type t = struct { int x }\n" "since 1.2\n" "data int t\n", 1); tsoftassert(ctx->typedepth == -1); td = lookup_type_ver(ctx, "t", "1.1"); tassert(td == NULL); dd = lookup_toplevel_ver(ctx, "t", "1.2"); tassert(dd != NULL); free_ctx(ctx); } static void test_parse_type_miscbad(void) { struct CSlul *ctx = create_ctx(CSLUL_P_IFACE); struct TypeDecl *td; expect_error(CSLUL_E_IDENTEXISTS, 7, 6); expect_error(CSLUL_E_IDENTEXISTS, 5, 6); expect_error(CSLUL_E_EMPTYPARAMDEF, 3, 8); expect_error(CSLUL_E_IDENTIFIEREXPECTED, 1, 7); expect_error(CSLUL_E_MISSINGVERWITHAPIDEF, 1, 1); TEST_SOURCE("data T\n" "since 1.1\n" "type T<>\n" "since 1.1\n" "type T<>\n" "since 1.2\n" "type T = int\n", 1); tsoftassert(ctx->typedepth == -1); td = lookup_type_ver(ctx, "T", "1.1"); tassert(td != NULL); tassert(td->type.type == T_PRIVATE); /* because there is no = */ free_ctx(ctx); } static const char typedepth_prefix[] = "func f()\n" "{\n" " "; static const char typedepth_postfix[] = "a\n" " {\n" " if 0 == 0 return\n" " }\n" "}\n"; #define TYPEDEPTH_TEST(s, erroffset) \ static const char repeat[] = s; \ /* 1 extra byte is needed because TEST_SOURCE expects that */ \ char buff[sizeof(typedepth_prefix)-1 + \ (sizeof(repeat)-1)*(MAXTYPEDEPTH-1) + \ sizeof(typedepth_postfix)-1+1]; \ typedepth_test(s, sizeof(s)-1, buff, erroffset); static void typedepth_test(const char *repeat, size_t len, char *buff, int erroffset) { size_t repeatlen = len*(MAXTYPEDEPTH-1); int i; char *p; memcpy(buff, typedepth_prefix, sizeof(typedepth_prefix)-1); p = buff+sizeof(typedepth_prefix)-1; for (i = 0; i < MAXTYPEDEPTH-1; i++) { memcpy(p, repeat, len); p += len; } memcpy(buff+sizeof(typedepth_prefix)-1+repeatlen, typedepth_postfix, sizeof(typedepth_postfix)-1); expect_error(CSLUL_E_TYPETOODEEP, 3, 4+repeatlen+1+erroffset); #undef REPEATLEN } static void test_parse_type_toodeep_struct(void) { struct CSlul *ctx = create_ctx(CSLUL_P_IMPL); TYPEDEPTH_TEST("struct { ", -2) TEST_SOURCE(buff, 1); free_ctx(ctx); } static void test_parse_type_toodeep_generic(void) { struct CSlul *ctx = create_ctx(CSLUL_P_IMPL); TYPEDEPTH_TEST("ref T<", -1) TEST_SOURCE(buff, 1); free_ctx(ctx); } static void test_parse_type_enum_nested(void) { struct CSlul *ctx = create_ctx(CSLUL_P_IMPL); expect_error(CSLUL_E_ENUMOUTSIDETYPEDEF, 1, 19); TEST_SOURCE("type E = struct { enum { a b } x }\n", 1); tsoftassert(ctx->typedepth == -1); free_ctx(ctx); } static void test_parse_func_noargs(void) { struct CSlul *ctx = create_ctx(CSLUL_P_IFACE); struct IdentDecl *f; TEST_SOURCE("since 1.0.1\n" "func f()\n", 1); f = lookup_toplevel_ver(ctx, "f", "1.0.1"); tassert(f != NULL); ASSERT_LINECOL(&f->ident, 2, 6); tsoftassert(f->u.funcbody == NULL); tsoftassert(f->type.type == T_FUNC); tsoftassert(f->type.quals == Q_CLOSED); tsoftassert(f->type.misc == M_VOIDRETURN); tassert(f->type.u.func != NULL); tsoftassert(f->type.u.func->returntype.type == T_INTERNAL); tsoftassert(f->type.u.func->returntype.u.internal == IT_Void); tsoftassert(f->type.u.func->params.first == NULL); tsoftassert(f->type.u.func->params.fields_root == NULL); tsoftassert(f->type.u.func->params.count == 0); tsoftassert(ctx->typedepth == -1); free_ctx(ctx); } static void test_parse_type_generic_define_dupl(void) { struct CSlul *ctx = create_ctx(CSLUL_P_IMPL); expect_error(CSLUL_E_IDENTEXISTS, 1, 21); TEST_SOURCE("type Abc = struct { }\n", 1); tsoftassert(ctx->typedepth == -1); free_ctx(ctx); } static void test_parse_type_generic_define_empty1(void) { struct CSlul *ctx = create_ctx(CSLUL_P_IMPL); expect_error(CSLUL_E_EMPTYPARAMDEF, 1, 10); TEST_SOURCE("type Abc<> = struct { }\n", 1); tsoftassert(ctx->typedepth == -1); free_ctx(ctx); } static void test_parse_type_generic_define_empty2(void) { struct CSlul *ctx = create_ctx(CSLUL_P_IMPL); expect_error(CSLUL_E_EMPTYPARAMDEF, 1, 16); TEST_SOURCE("type Abc = struct { }\n", 1); tsoftassert(ctx->typedepth == -1); free_ctx(ctx); } static void test_parse_type_generic_define_badtype(void) { struct CSlul *ctx = create_ctx(CSLUL_P_IMPL); expect_error(CSLUL_E_BADPARAMTYPE, 1, 10); TEST_SOURCE("type Map<[2]T> = struct { }\n", 1); tsoftassert(ctx->typedepth == -1); free_ctx(ctx); } static void test_parse_type_generic_define_badend(void) { struct CSlul *ctx = create_ctx(CSLUL_P_IMPL); expect_error(CSLUL_E_PARAMDEFEND, 1, 16); TEST_SOURCE("type Maptypedepth == -1); free_ctx(ctx); } static void test_parse_func_noreturn(void) { struct CSlul *ctx = create_ctx(CSLUL_P_IFACE); struct IdentDecl *f; TEST_SOURCE("since 1.0\n" "func f() noreturn\n", 1); f = lookup_toplevel_ver(ctx, "f", "1.0"); tassert(f != NULL); ASSERT_LINECOL(&f->ident, 2, 6); tsoftassert(f->u.funcbody == NULL); tsoftassert(f->type.type == T_FUNC); tsoftassert(f->type.quals == Q_CLOSED); tsoftassert(f->type.misc == M_NORETURN); tassert(f->type.u.func != NULL); tsoftassert(f->type.u.func->returntype.type == 0); tsoftassert(f->type.u.func->params.first == NULL); tsoftassert(f->type.u.func->params.fields_root == NULL); tsoftassert(f->type.u.func->params.count == 0); tsoftassert(ctx->typedepth == -1); free_ctx(ctx); } static void test_parse_func_1arg(void) { struct CSlul *ctx = create_ctx(CSLUL_P_IFACE); struct IdentDecl *f, *b; TEST_SOURCE("since 1.0\n" "func f(bool b) -> int\n", 1); f = lookup_toplevel_ver(ctx, "f", "1.0"); tassert(f != NULL); ASSERT_LINECOL(&f->ident, 2, 6); tsoftassert(f->u.funcbody == NULL); tsoftassert(f->type.type == T_FUNC); tsoftassert(f->type.quals == Q_CLOSED); tsoftassert(f->type.misc == 0); tassert(f->type.u.func != NULL); ASSERT_TYPE_INT(&f->type.u.func->returntype); tsoftassert(f->type.u.func->returntype.defflags == 0); tassert(f->type.u.func->params.first != NULL); tassert(f->type.u.func->params.fields_root != NULL); tassert(f->type.u.func->params.fields_root == &f->type.u.func->params.first->f.vardef.decl.ident); tsoftassert(f->type.u.func->params.first->next == NULL); tsoftassert(f->type.u.func->params.count == 1); b = (struct IdentDecl*)lookup(ctx,f->type.u.func->params.fields_root,"b"); tassert(b); ASSERT_TYPE_BOOL(&b->type); ASSERT_LINECOL(&b->ident, 2, 13); tsoftassert(b->u.initval == NULL); tsoftassert(ctx->typedepth == -1); /* Ensure that the parameter doesn't become visible in the toplevel namespace */ tsoftassert(lookup_toplevel(ctx, "b") == NULL); free_ctx(ctx); } static void test_parse_func_2args(void) { struct CSlul *ctx = create_ctx(CSLUL_P_IFACE); struct IdentDecl *f; struct VarDef *b, *c; TEST_SOURCE("since 1.0\n" "func f(bool b, byte c) -> int\n", 1); f = lookup_toplevel_ver(ctx, "f", "1.0"); tassert(f != NULL); ASSERT_LINECOL(&f->ident, 2, 6); tsoftassert(f->u.funcbody == NULL); tsoftassert(f->type.type == T_FUNC); tsoftassert(f->type.quals == Q_CLOSED); tsoftassert(f->type.misc == 0); tassert(f->type.u.func != NULL); ASSERT_TYPE_INT(&f->type.u.func->returntype); tsoftassert(f->type.u.func->returntype.defflags == 0); tassert(f->type.u.func->params.first != NULL); tassert(f->type.u.func->params.fields_root != NULL); tassert(f->type.u.func->params.first->next != NULL); tsoftassert(f->type.u.func->params.first->next->next == NULL); tsoftassert(f->type.u.func->params.count == 2); b = (struct VarDef*)lookup(ctx,f->type.u.func->params.fields_root,"b"); c = (struct VarDef*)lookup(ctx,f->type.u.func->params.fields_root,"c"); tassert(b && c); ASSERT_TYPE_BOOL(&b->decl.type); ASSERT_LINECOL(&b->decl.ident, 2, 13); tsoftassert(b->decl.u.initval == NULL); tsoftassert(b->var_id == 0); ASSERT_TYPE_BYTE(&c->decl.type); ASSERT_LINECOL(&c->decl.ident, 2, 21); tsoftassert(c->decl.u.initval == NULL); tsoftassert(c->var_id == 1); tsoftassert(ctx->typedepth == -1); free_ctx(ctx); } static void test_parse_func_duplparam(void) { struct CSlul *ctx = create_ctx(CSLUL_P_IFACE); expect_error(CSLUL_E_DUPLFUNCPARAM, 2, 21); TEST_SOURCE("since 1.0\n" "func f(bool b, bool b) -> int\n", 1); tsoftassert(ctx->typedepth == -1); free_ctx(ctx); } static void test_parse_func_badquals(void) { struct CSlul *ctx = create_ctx(CSLUL_P_IFACE); expect_error(CSLUL_E_QUALNOTALLOWED, 6, 13); expect_error(CSLUL_E_QUALNOTALLOWED, 4, 8); expect_error(CSLUL_E_QUALNOTALLOWED, 2, 8); TEST_SOURCE("since 1.0\n" "func f(var int x) -> int\n" "since 1.0\n" "func g(threaded ref S y)\n" "since 1.0\n" "func h() -> var int\n", 1); tsoftassert(ctx->typedepth == -1); free_ctx(ctx); } static void test_parse_func_missingfuncbody(void) { struct CSlul *ctx = create_ctx(CSLUL_P_IMPL); /* A function body is always required in impl files */ expect_error(CSLUL_E_IMPLFUNCWITHOUTBODY, 1, 16); TEST_SOURCE("func f() -> int\n", 1); tsoftassert(ctx->typedepth == -1); free_ctx(ctx); } static void test_parse_func_versioned(void) { struct CSlul *ctx = create_ctx(CSLUL_P_IFACE); struct IdentDecl *f, *g; TEST_SOURCE("since 1.1\n" "func f() -> int\n" "since 1.1\n" "func g() -> byte\n", 1); tsoftassert(ctx->typedepth == -1); f = lookup_toplevel_ver(ctx, "f", "1.1"); g = lookup_toplevel_ver(ctx, "g", "1.1"); tassert(f != NULL); tassert(g != NULL); tassert(f != g); ASSERT_LINECOL(&f->ident, 2, 6); ASSERT_LINECOL(&g->ident, 4, 6); tsoftassert(f->type.defflags == (D_DEFINED|D_FUNC)); tsoftassert(g->type.defflags == (D_DEFINED|D_FUNC)); tassert(f->type.type == T_FUNC); tassert(g->type.type == T_FUNC); tassert(f->type.u.func != NULL); tassert(g->type.u.func != NULL); ASSERT_TYPE_INT(&f->type.u.func->returntype); ASSERT_TYPE_BYTE(&g->type.u.func->returntype); tsoftassert(f->u.funcbody == NULL); tsoftassert(g->u.funcbody == NULL); free_ctx(ctx); } static void test_parse_func_missingversion1(void) { struct CSlul *ctx = create_ctx(CSLUL_P_IFACE); expect_error(CSLUL_E_NOSINCEVERSION, 1, 6); TEST_SOURCE("since\n", 1); tsoftassert(ctx->typedepth == -1); free_ctx(ctx); } static void test_parse_func_missingversion2(void) { struct CSlul *ctx = create_ctx(CSLUL_P_IFACE); /* TODO report only one error */ expect_error(CSLUL_E_MISSINGVERWITHAPIDEF, 2, 1); expect_error(CSLUL_E_BADTOPLEVELCONTINUATION, 1, 6); expect_error(CSLUL_E_NOSINCEVERSION, 1, 6); TEST_SOURCE("since)\n" "func f() -> int\n", 1); tsoftassert(ctx->typedepth == -1); free_ctx(ctx); } static void test_parse_func_missingversion3(void) { struct CSlul *ctx = create_ctx(CSLUL_P_IFACE); /* FIXME report only one error. perhaps CSLUL_E_UNTERMINATEDSTRING should only be reported if a string is expected? */ expect_error(CSLUL_E_BADTOPLEVELCONTINUATION, 1, 7); expect_error(CSLUL_E_NOSINCEVERSION, 1, 7); expect_error(CSLUL_E_UNTERMINATEDSTRING, 1, 8); TEST_SOURCE("since \"\n", 1); tsoftassert(ctx->typedepth == -1); free_ctx(ctx); } static void test_parse_func_missingversion4(void) { struct CSlul *ctx; struct CSlulConfig *cfg; cfg = tcfg(cslul_config_create(NULL)); cslul_config_set_message_level(cfg, CSLUL_L_FATAL); ctx = create_ctx_cfg(CSLUL_P_IFACE, cfg); TEST_SOURCE("since \xC3" "1\nfunc f()\n", 1); tsoftassert(ctx->typedepth == -1); free_ctx(ctx); } static void test_parse_func_unversioned(void) { struct CSlul *ctx = create_ctx(CSLUL_P_IFACE); expect_error(CSLUL_E_MISSINGVERWITHAPIDEF, 1, 1); TEST_SOURCE("func f() -> int\n", 1); tsoftassert(ctx->typedepth == -1); free_ctx(ctx); } static void test_parse_func_nosuchversion(void) { struct CSlul *ctx = create_ctx(CSLUL_P_IFACE); ctx->parsed_module->num_apidefs = 0; expect_error(CSLUL_E_NOSUCHVERSION, 1, 7); TEST_SOURCE("since 1.6\n" "func f(int x) -> int\n", 1); tsoftassert(ctx->typedepth == -1); free_ctx(ctx); } static void test_parse_func_emptybody(void) { struct CSlul *ctx = create_ctx(CSLUL_P_IMPL); ctx->current_filename = "test.slul"; TEST_SOURCE("func f(bool b)\n" "{\n}\n", 1); tassert(ctx->funcbody != NULL); tassert(ctx->funcbody->has_retval == 0); tassert(ctx->funcbody->stmtblock.stmt.type == S_NOP); tassert(ctx->funcbody->stmtblock.idents == NULL); tassert(ctx->funcbody->filename == ctx->current_filename); tassert(ctx->funcbody->next == NULL); tsoftassert(ctx->blockdepth == -1); free_ctx(ctx); } static void test_parse_func_generic(void) { struct CSlul *ctx = create_ctx(CSLUL_P_IMPL); struct TypeDecl *prmT, *prmU; struct Type *basetype; struct PrmDefEntry *prmentry; struct IdentDecl *f, *p, *v; struct Type *rt; struct Stmt *s; struct ExprNode *e; TEST_SOURCE( "func work(slot T funcparam) -> ?slot U\n" "{\n" " slot U somelocalvar\n" " assert funcparam == funcparam\n" " return none\n" "}\n", 1); /* work() */ f = lookup_toplevel(ctx, "work"); tassert(f != NULL); tsoftassert(f->type.defflags == (D_DEFINED|D_FUNC)); tassert(f->type.type == T_GENERICDEF); tsoftassert(f->type.quals == Q_CLOSED); tassert(f->type.u.gdef != NULL); tassert(f->type.u.gdef->params_root != NULL); prmentry = &f->type.u.gdef->paramdef; tassert(prmentry->prmtype == PT_ANY); ASSERT_IDENT_L(&prmentry->paramdecl.ident, 1, 11, "T"); prmT = &prmentry->paramdecl; tassert(prmentry->next != NULL); prmentry = prmentry->next; tassert(prmentry->prmtype == (PT_REFTYPE|PT_OPTIONAL)); ASSERT_IDENT_L(&prmentry->paramdecl.ident, 1, 19, "U"); prmU = &prmentry->paramdecl; tsoftassert(prmentry->next == NULL); basetype = &f->type.u.gdef->basetype; tassert(basetype->type == T_FUNC); tassert(basetype->u.func != NULL); tassert(basetype->u.func->params.fields_root != NULL); tsoftassert(basetype->u.func->params.count == 1); /* Parameter: ref T funcparam */ p = (struct IdentDecl*)lookup(ctx, basetype->u.func->params.fields_root, "funcparam"); tassert(p != NULL); tassert(p->type.type == T_SLOT); tsoftassert(p->type.quals == IMPLQUALS); tsoftassert(p->type.misc == M_NORMAL); tsoftassert(p->type.u.ident == prmT); /* Return: ?slot U */ rt = &basetype->u.func->returntype; tassert(rt->type == T_SLOT); tsoftassert(rt->quals == IMPLQUALS); tsoftassert(rt->misc == PT_OPTIONAL); tsoftassert(rt->u.ident == prmU); /* Local variable: slot U somelocalvar */ tassert(f->u.funcbody != NULL); tsoftassert(f->u.funcbody->has_retval == 1); tsoftassert(f->u.funcbody->stmtblock.idents != NULL); v = (struct IdentDecl*)lookup(ctx, f->u.funcbody->stmtblock.idents, "somelocalvar"); tassert(v != NULL); tassert(f->u.funcbody->stmtblock.stmt.type == S_DECL); tsoftassert(v == &f->u.funcbody->stmtblock.stmt.u.vardef->decl); tassert(v->type.type == T_SLOT); tsoftassert(v->type.misc == 0); tsoftassert(v->type.u.ident == prmU); s = &f->u.funcbody->stmtblock.stmt; ASSERT_STMT_DECL(s, "somelocalvar"); tsoftassert(&s->u.vardef->decl == v); s = s->next; /* Expression referencing the type-generic parameter */ tassert(s != NULL); tassert(s->type == S_ASSERT); tassert(s->u.expr != NULL); tassert((e = s->u.expr->root) != NULL); tassert(e->exprtype == E_BINARYOP); tassert((e = e->a.expr) != NULL); ASSERT_EXPR_IDENT(e, "funcparam"); tassert(e->a.ident == &p->ident); free_ctx(ctx); } static void test_parse_func_lifetime_good1(void) { struct IdentDecl *f, *t; struct CSlul *ctx = create_ctx(CSLUL_P_IFACE); TEST_SOURCE("since 1.0\n" "func f(ref T t) -> own T\n" " lifetime t >= return\n", 1); tsoftassert(ctx->typedepth == -1); f = lookup_toplevel_ver(ctx, "f", "1.0"); tassert(f != NULL); ASSERT_LINECOL(&f->ident, 2, 6); tsoftassert(f->u.funcbody == NULL); tsoftassert(f->type.defflags == (D_DEFINED|D_FUNC)); tsoftassert(f->type.type == T_FUNC); tsoftassert(f->type.quals == Q_CLOSED); tsoftassert(f->type.misc == 0); tassert(f->type.u.func != NULL); /* TODO check that type is wrapped (should the parameter or the return be wrapped?) */ /*ASSERT_TYPE_REF(&f->type.u.func->returntype); ASSERT_TYPE_LIFETIME(&f->type.u.func->returntype);*/ tsoftassert(f->type.u.func->returntype.defflags == 0); tassert(f->type.u.func->params.first != NULL); tassert(f->type.u.func->params.fields_root != NULL); tassert(f->type.u.func->params.fields_root == &f->type.u.func->params.first->f.vardef.decl.ident); tsoftassert(f->type.u.func->params.first->next == NULL); tsoftassert(f->type.u.func->params.count == 1); t = (struct IdentDecl*)lookup(ctx,f->type.u.func->params.fields_root,"t"); tassert(t); /* TODO check that type is wrapped (should the parameter or the return be wrapped?) */ /*ASSERT_TYPE_REF(&t->type); ASSERT_TYPE_LIFETIME(&t->type);*/ ASSERT_LINECOL(&t->ident, 2, 14); tsoftassert(t->u.initval == NULL); free_ctx(ctx); } static void test_parse_func_lifetime_good2(void) { struct IdentDecl *f, *t, *u; struct CSlul *ctx = create_ctx(CSLUL_P_IFACE); TEST_SOURCE("since 1.0\n" "func f(ref T t, ref U u)\n" " lifetime u >= t\n", 1); tsoftassert(ctx->typedepth == -1); f = lookup_toplevel_ver(ctx, "f", "1.0"); tassert(f != NULL); ASSERT_LINECOL(&f->ident, 2, 6); tsoftassert(f->u.funcbody == NULL); tsoftassert(f->type.type == T_FUNC); tsoftassert(f->type.quals == Q_CLOSED); tsoftassert(f->type.misc == M_VOIDRETURN); tassert(f->type.u.func != NULL); tsoftassert(f->type.u.func->returntype.type == T_INTERNAL); tsoftassert(f->type.u.func->returntype.u.internal == IT_Void); tassert(f->type.u.func->params.first != NULL); tassert(f->type.u.func->params.fields_root != NULL); tassert(f->type.u.func->params.fields_root == &f->type.u.func->params.first->f.vardef.decl.ident); tassert(f->type.u.func->params.first->next != NULL); tsoftassert(f->type.u.func->params.first->next->next == NULL); tsoftassert(f->type.u.func->params.count == 2); t = (struct IdentDecl*)lookup(ctx,f->type.u.func->params.fields_root,"t"); tassert(t); /* TODO check that type is wrapped (which parameter should be wrapped?) */ /*ASSERT_TYPE_REF(&t->type); ASSERT_TYPE_LIFETIME(&t->type);*/ ASSERT_LINECOL(&t->ident, 2, 14); u = (struct IdentDecl*)lookup(ctx,f->type.u.func->params.fields_root,"u"); tassert(u); /* TODO check that type is wrapped (which parameter should be wrapped?) */ /*ASSERT_TYPE_REF(&t->type); ASSERT_TYPE_LIFETIME(&t->type);*/ ASSERT_LINECOL(&u->ident, 2, 23); tsoftassert(t->u.initval == NULL); free_ctx(ctx); } static void test_parse_func_lifetime_good_multiple(void) { struct IdentDecl *f, *t, *u; struct CSlul *ctx = create_ctx(CSLUL_P_IFACE); TEST_SOURCE("since 1.0\n" "func f(ref T t, ref U u) -> ref T\n" " lifetime u >= t\n" " lifetime u >= return\n", 1); tsoftassert(ctx->typedepth == -1); f = lookup_toplevel_ver(ctx, "f", "1.0"); tassert(f != NULL); ASSERT_LINECOL(&f->ident, 2, 6); tsoftassert(f->u.funcbody == NULL); tsoftassert(f->type.type == T_FUNC); tsoftassert(f->type.quals == Q_CLOSED); tsoftassert(f->type.misc == 0); tassert(f->type.u.func != NULL); /* TODO check that type is wrapped (should the parameter or the return be wrapped?) */ /*ASSERT_TYPE_REF(&f->type.u.func->returntype); ASSERT_TYPE_LIFETIME(&f->type.u.func->returntype);*/ tsoftassert(f->type.u.func->returntype.defflags == 0); tassert(f->type.u.func->params.first != NULL); tassert(f->type.u.func->params.fields_root != NULL); tassert(f->type.u.func->params.fields_root == &f->type.u.func->params.first->f.vardef.decl.ident); tassert(f->type.u.func->params.first->next != NULL); tsoftassert(f->type.u.func->params.first->next->next == NULL); tsoftassert(f->type.u.func->params.count == 2); t = (struct IdentDecl*)lookup(ctx,f->type.u.func->params.fields_root,"t"); tassert(t); /* TODO check that type is wrapped (which parameter should be wrapped?) */ /*ASSERT_TYPE_REF(&t->type); ASSERT_TYPE_LIFETIME(&t->type);*/ ASSERT_LINECOL(&t->ident, 2, 14); u = (struct IdentDecl*)lookup(ctx,f->type.u.func->params.fields_root,"u"); tassert(u); /* TODO check that type is wrapped (which parameter should be wrapped?) */ /*ASSERT_TYPE_REF(&t->type); ASSERT_TYPE_LIFETIME(&t->type);*/ ASSERT_LINECOL(&u->ident, 2, 23); tsoftassert(t->u.initval == NULL); free_ctx(ctx); } static void test_parse_func_lifetime_truncated(void) { struct CSlul *ctx = create_ctx(CSLUL_P_IFACE); ctx->parsed_module->is_unstable = 1; expect_error(CSLUL_E_LIFETIMENONIDENTTOK, 2, 13); TEST_SOURCE("func f(ref T t) -> own T\n" " lifetime\n", 1); tsoftassert(ctx->typedepth == -1); free_ctx(ctx); } static void test_parse_func_lifetime_badident1(void) { struct CSlul *ctx = create_ctx(CSLUL_P_IFACE); ctx->parsed_module->is_unstable = 1; expect_error(CSLUL_E_LIFETIMENONIDENTTOK, 2, 14); TEST_SOURCE("func f(ref T t) -> own T\n" " lifetime if\n", 1); tsoftassert(ctx->typedepth == -1); free_ctx(ctx); } static void test_parse_func_lifetime_badrel(void) { struct CSlul *ctx = create_ctx(CSLUL_P_IFACE); expect_error(CSLUL_E_LIFETIMENONGREATERTOK, 3, 16); TEST_SOURCE("since 1.0\n" "func f(ref T t) -> own T\n" " lifetime t <= return\n", 1); tsoftassert(ctx->typedepth == -1); free_ctx(ctx); } static void test_parse_func_lifetime_badident2(void) { struct CSlul *ctx = create_ctx(CSLUL_P_IFACE); expect_error(CSLUL_E_LIFETIMENONIDENTTOK, 3, 19); TEST_SOURCE("since 1.0\n" "func f(ref T t) -> own T\n" " lifetime t >= if\n", 1); tsoftassert(ctx->typedepth == -1); free_ctx(ctx); } static void test_parse_func_lifetime_dup_return(void) { struct CSlul *ctx = create_ctx(CSLUL_P_IFACE); expect_error(CSLUL_E_LIFETIMESAMEPARAM, 3, 24); TEST_SOURCE("since 1.0\n" "func f(ref T t) -> own T\n" " lifetime return >= return\n", 1); tsoftassert(ctx->typedepth == -1); free_ctx(ctx); } static void test_parse_func_lifetime_dup_param(void) { struct CSlul *ctx = create_ctx(CSLUL_P_IFACE); expect_error(CSLUL_E_LIFETIMESAMEPARAM, 3, 19); TEST_SOURCE("since 1.0\n" "func f(ref T t) -> own T\n" " lifetime t >= t\n", 1); tsoftassert(ctx->typedepth == -1); free_ctx(ctx); } static void test_parse_func_lifetime_nonexisting1(void) { struct CSlul *ctx = create_ctx(CSLUL_P_IFACE); expect_error(CSLUL_E_LIFETIMEIDENTNOTFOUND, 3, 14); TEST_SOURCE("since 1.0\n" "func f(ref T t) -> own T\n" " lifetime z >= t\n", 1); tsoftassert(ctx->typedepth == -1); free_ctx(ctx); } static void test_parse_func_lifetime_nonexisting2(void) { struct CSlul *ctx = create_ctx(CSLUL_P_IFACE); expect_error(CSLUL_E_LIFETIMEIDENTNOTFOUND, 3, 19); TEST_SOURCE("since 1.0\n" "func f(ref T t) -> own T\n" " lifetime t >= z\n", 1); tsoftassert(ctx->typedepth == -1); free_ctx(ctx); } static void test_parse_func_lifetime_nonref(void) { struct CSlul *ctx = create_ctx(CSLUL_P_IFACE); expect_error(CSLUL_E_LIFETIMENONREF, 3, 19); TEST_SOURCE("since 1.0\n" "func f(ref T t, int u) -> own T\n" " lifetime t >= u\n", 1); tsoftassert(ctx->typedepth == -1); free_ctx(ctx); } static void test_parse_typeident_data(void) { struct CSlul *ctx = create_ctx(CSLUL_P_IFACE); struct TypeDecl *t; struct TopLevelIdent *tident; struct ApiDef *ver10; TEST_SOURCE("since 1.0\n" "data Point .origin = (0, 0)\n", 1); ver10 = (struct ApiDef *)lookup(ctx, ctx->module.apidefs_root, "1.0"); /* "origin" should now be a typeidentifier in "Point" */ t = lookup_type(ctx, "Point"); tassert(t != NULL); tassert(t->typeidents != NULL); tident = (struct TopLevelIdent *)lookup(ctx, t->typeidents, "origin"); tassert(tident != NULL); tassert(tident->decl.type.type == T_IDENT); tassert(tident->sinceversions != NULL); tsoftassert(tident->sinceversions->next == NULL); tsoftassert(tident->sinceversions->version == ver10); tsoftassert(lookup_toplevel(ctx, "origin") == NULL); /* it is a typeident */ free_ctx(ctx); } static void test_parse_typeident_data_versioned(void) { struct CSlul *ctx = create_ctx(CSLUL_P_IFACE); struct TypeDecl *t; struct TopLevelIdent *ti1, *ti2; struct ApiDef *ver10, *ver12; TEST_SOURCE("since 1.0\n" "data Thing .ti1 = (1, 2, .blue)\n" "since 1.2\n" "data Thing .ti2 = (0x32, 0x21, .red)\n", 1); ver10 = (struct ApiDef *)lookup(ctx, ctx->module.apidefs_root, "1.0"); ver12 = (struct ApiDef *)lookup(ctx, ctx->module.apidefs_root, "1.2"); tassert(ver10 != NULL); tassert(ver12 != NULL); t = lookup_type(ctx, "Thing"); tassert(t != NULL); tassert(t->typeidents != NULL); /* ti1 */ ti1 = (struct TopLevelIdent *)lookup(ctx, t->typeidents, "ti1"); tassert(ti1 != NULL); tsoftassert(ti1->decl.type.type == T_IDENT); tassert(ti1->sinceversions != NULL); tsoftassert(ti1->sinceversions->next == NULL); tsoftassert(ti1->sinceversions->version == ver10); /* ti2 */ ti2 = (struct TopLevelIdent *)lookup(ctx, t->typeidents, "ti2"); tassert(ti2 != NULL); tsoftassert(ti2->decl.type.type == T_IDENT); tassert(ti2->sinceversions != NULL); tsoftassert(ti2->sinceversions->next == NULL); tsoftassert(ti2->sinceversions->version == ver12); tsoftassert(lookup_toplevel(ctx, "ti1") == NULL); /* it is a typeident */ tsoftassert(lookup_toplevel(ctx, "ti2") == NULL); free_ctx(ctx); } static void test_parse_typeident_data_badeof(void) { struct CSlul *ctx = create_ctx(CSLUL_P_IMPL); expect_error(CSLUL_E_IDENTIFIEREXPECTED, 1, 13); TEST_SOURCE("data Thing .\n", 1); free_ctx(ctx); } /* TODO it would be nice to support type identifiers in builtin types also */ static void test_parse_typeident_data_notappl(void) { struct CSlul *ctx = create_ctx(CSLUL_P_IFACE); expect_error(CSLUL_E_NOTYPESCOPEDATA, 2, 13); TEST_SOURCE("since 1.0\n" "data uint32 .million = 1_000_000\n", 1); free_ctx(ctx); } static void test_parse_typeident_data_dupl(void) { struct CSlul *ctx = create_ctx(CSLUL_P_IMPL); expect_error(CSLUL_E_IDENTEXISTS, 2, 13); TEST_SOURCE("data Thing .t = (1, 2)\n" "data Thing .t = (2, 1)\n", 1); free_ctx(ctx); } static void test_parse_typeident_data_verdupl(void) { struct CSlul *ctx = create_ctx(CSLUL_P_IFACE); expect_error(CSLUL_E_IDENTEXISTS, 4, 13); TEST_SOURCE("since 1.0\n" "data Thing .t = (1, 2)\n" "since 1.0\n" "data Thing .t = (2, 1)\n", 1); free_ctx(ctx); } static void test_parse_typeident_data_vermix1(void) { struct CSlul *ctx = create_ctx(CSLUL_P_IFACE); expect_error(CSLUL_E_IDENTEXISTS, 3, 13); expect_error(CSLUL_E_MISSINGVERWITHAPIDEF, 3, 1); TEST_SOURCE("since 1.0\n" "data Thing .t = (1, 2)\n" "data Thing .t = (2, 1)\n", 1); free_ctx(ctx); } static void test_parse_typeident_data_vermix2(void) { struct CSlul *ctx = create_ctx(CSLUL_P_IFACE); expect_error(CSLUL_E_IDENTEXISTS, 3, 13); expect_error(CSLUL_E_MISSINGVERWITHAPIDEF, 1, 1); TEST_SOURCE("data Thing .t = (1, 2)\n" "since 1.2\n" "data Thing .t = (2, 1)\n", 1); free_ctx(ctx); } static void assert_func(struct IdentDecl *func, size_t num_params, unsigned returntype) { tassert(func != NULL); tassert(func->type.type == T_FUNC); tassert(func->type.u.func != NULL); tassert(func->type.u.func->params.count == num_params); tassert(func->type.u.func->returntype.type == returntype); if (num_params) { tsoftassert(func->type.u.func->params.first != NULL); } } static void test_parse_typeident_func_nonref(void) { struct CSlul *ctx = create_ctx(CSLUL_P_IFACE); struct TypeDecl *t; struct IdentDecl *tident; /* XXX should this be written as "-> stack Color"? or "-> val Color"? */ TEST_SOURCE("since 1.0\n" "func .gray(int intensity) -> Color\n", 1); /* "gray" should now be a typeidentifier in "Color" */ t = lookup_type(ctx, "Color"); tassert(t != NULL); tassert(t->typeidents != NULL); tident = (struct IdentDecl *)lookup(ctx, t->typeidents, "gray"); assert_func(tident, 1, T_IDENT); tassert(tident->type.u.func->returntype.u.ident != NULL); ASSERT_IDENT(&tident->type.u.func->returntype.u.ident->ident, "Color"); tsoftassert(lookup_toplevel(ctx, "gray") == NULL); free_ctx(ctx); } static void test_parse_typeident_func_ref(void) { struct CSlul *ctx = create_ctx(CSLUL_P_IFACE); struct TypeDecl *t; struct IdentDecl *tident; /* TODO add some syntax for pure arena parameters. perhaps just "arena"? */ TEST_SOURCE("since 1.0\n" "func .new(arena Dummy ar) -> arena Thing\n", 1); /* "new" should now be a typeidentifier in "Thing" */ t = lookup_type(ctx, "Thing"); tassert(t != NULL); tassert(t->typeidents != NULL); tident = (struct IdentDecl *)lookup(ctx, t->typeidents, "new"); assert_func(tident, 1, T_REF); tassert(tident->type.u.func->returntype.misc == M_ARENA); tsoftassert(lookup_toplevel(ctx, "new") == NULL); free_ctx(ctx); } static void test_parse_typeident_func_ref_versioned(void) { struct CSlul *ctx = create_ctx(CSLUL_P_IFACE); struct TypeDecl *t; struct TopLevelIdent *func; struct ApiDef *ver10; TEST_SOURCE("since 1.0\n" "func .smallnum(int i) -> ref BigInt\n", 1); /* "smallnum" should now be a typeidentifier in "BigInt" */ ver10 = (struct ApiDef *)lookup(ctx, ctx->module.apidefs_root, "1.0"); t = lookup_type(ctx, "BigInt"); tassert(t != NULL); tassert(t->typeidents != NULL); func = (struct TopLevelIdent *)lookup(ctx, t->typeidents, "smallnum"); tassert(func != NULL); assert_func(&func->decl, 1, T_REF); tassert(func->decl.type.u.func->returntype.misc == M_NORMAL); ASSERT_TYPE_INT(&func->decl.type.u.func->params.first->f.vardef.decl.type); tassert(func->sinceversions != NULL); tsoftassert(func->sinceversions->next == NULL); tsoftassert(func->sinceversions->version == ver10); tsoftassert(lookup_toplevel(ctx, "smallnum") == NULL); free_ctx(ctx); } static void test_parse_typeident_func_parametric_versioned(void) { struct CSlul *ctx = create_ctx(CSLUL_P_IFACE); struct TypeDecl *t; struct TopLevelIdent *func, *otherfunc; struct Type *funcbase; /* base type of parametric function type */ struct Type *param; struct ApiDef *ver10; TEST_SOURCE("since 1.0\n" "func .some_long_function_name(slot T x, int y) -> ref Thing\n" "since 1.0\n" "func .otherfunc(byte b) -> ref Thing\n", 1); ver10 = (struct ApiDef *)lookup(ctx, ctx->module.apidefs_root, "1.0"); t = lookup_type(ctx, "Thing"); tassert(t != NULL); tassert(t->typeidents != NULL); /* Typeidentifier "some_long_function_name" in type "Thing" */ func = (struct TopLevelIdent *)lookup(ctx, t->typeidents, "some_long_function_name"); tassert(func != NULL); tassert(func->sinceversions != NULL); tsoftassert(func->sinceversions->next == NULL); tsoftassert(func->sinceversions->version == ver10); tassert(func->decl.type.type == T_GENERICDEF); tassert(func->decl.type.u.gdef != NULL); tsoftassert(func->decl.type.u.gdef->paramdef.prmtype == PT_REFTYPE); tsoftassert(func->decl.type.u.gdef->paramdef.paramdecl.type.type == T_GENERICVAR); tsoftassert(func->decl.type.u.gdef->paramdef.next == NULL); tsoftassert(func->decl.type.u.gdef->params_root == &func->decl.type.u.gdef->paramdef.paramdecl.ident); funcbase = &func->decl.type.u.gdef->basetype; tassert(funcbase->type == T_FUNC); tassert(funcbase->u.func != NULL); tassert(funcbase->u.func->params.count == 2); tassert(funcbase->u.func->params.first != NULL); param = &funcbase->u.func->params.first->f.vardef.decl.type; tassert(param->type == T_SLOT); tassert(param->misc == 0); tassert(param->u.ident == &func->decl.type.u.gdef->paramdef.paramdecl); tassert(funcbase->u.func->returntype.type == T_REF); ASSERT_TYPE_REF(&funcbase->u.func->returntype, 0, M_NORMAL); ASSERT_TYPE_INT(&funcbase->u.func->params.first->next->f.vardef.decl.type); /* Typeidentifier "otherfunc" in type "Thing" */ otherfunc = (struct TopLevelIdent *)lookup(ctx, t->typeidents, "otherfunc"); tassert(otherfunc != NULL); tassert(otherfunc->sinceversions != NULL); tsoftassert(otherfunc->sinceversions->next == NULL); tsoftassert(otherfunc->sinceversions->version == ver10); assert_func(&otherfunc->decl, 1, T_REF); tassert(otherfunc->decl.type.u.func->returntype.misc == M_NORMAL); ASSERT_TYPE_BYTE(&otherfunc->decl.type.u.func->params.first->f.vardef.decl.type); tsoftassert(lookup_toplevel(ctx, "some_long_function_name") == NULL); tsoftassert(lookup_toplevel(ctx, "otherfunc") == NULL); tsoftassert(lookup_toplevel(ctx, "1.0") == NULL); free_ctx(ctx); } static void test_parse_typeident_func_badeof1(void) { struct CSlul *ctx = create_ctx(CSLUL_P_IMPL); expect_error(CSLUL_E_IDENTIFIEREXPECTED, 1, 7); TEST_SOURCE("func .\n", 1); free_ctx(ctx); } static void test_parse_typeident_func_badeof2(void) { struct CSlul *ctx = create_ctx(CSLUL_P_IMPL); expect_error(CSLUL_E_UNEXPECTEDEOF, 1, 8); TEST_SOURCE("func .f\n", 1); free_ctx(ctx); } static void test_parse_typeident_func_notappl(void) { struct CSlul *ctx = create_ctx(CSLUL_P_IFACE); expect_error(CSLUL_E_NOTYPESCOPEFUNC, 2, 7); TEST_SOURCE("since 1.0\n" "func .f()\n", 1); free_ctx(ctx); } static void test_parse_typeident_func_dupl(void) { struct CSlul *ctx = create_ctx(CSLUL_P_IMPL); expect_error(CSLUL_E_IDENTEXISTS, 2, 7); TEST_SOURCE("func .f() -> ?ref Thing { return none }\n" "func .f() -> ?ref Thing { return none }\n", 1); free_ctx(ctx); } static void test_parse_typeident_func_verdupl(void) { struct CSlul *ctx = create_ctx(CSLUL_P_IFACE); expect_error(CSLUL_E_IDENTEXISTS, 4, 7); TEST_SOURCE("since 1.1\n" "func .f() -> ref Thing\n" "since 1.2\n" "func .f() -> ref Thing\n" , 1); free_ctx(ctx); } static void test_parse_typeident_func_vermix1(void) { struct CSlul *ctx = create_ctx(CSLUL_P_IFACE); expect_error(CSLUL_E_IDENTEXISTS, 3, 7); expect_error(CSLUL_E_MISSINGVERWITHAPIDEF, 3, 1); TEST_SOURCE("since 1.1\n" "func .f() -> ref Thing\n" "func .f() -> ref Thing\n", 1); free_ctx(ctx); } static void test_parse_typeident_func_vermix2(void) { struct CSlul *ctx = create_ctx(CSLUL_P_IFACE); expect_error(CSLUL_E_IDENTEXISTS, 3, 7); expect_error(CSLUL_E_MISSINGVERWITHAPIDEF, 1, 1); TEST_SOURCE("func .f() -> ref Thing\n" "since 1.1\n" "func .f() -> ref Thing\n", 1); free_ctx(ctx); } static void test_parse_method(void) { struct TypeDecl *t; struct IdentDecl *f; struct CSlul *ctx = create_ctx(CSLUL_P_IMPL); TEST_SOURCE("func Thing.f() -> int { return 0 }\n", 1); t = lookup_type(ctx, "Thing"); tassert(t != NULL); tassert(t->typeidents != NULL); f = (struct IdentDecl *)lookup(ctx, t->typeidents, "f"); tassert(f != NULL); tassert(f->type.type == T_METHOD); tassert(f->type.misc == 0); free_ctx(ctx); } static void test_parse_method_versioned(void) { struct TypeDecl *t; struct TopLevelIdent *f; struct ApiDef *ver12; struct CSlul *ctx = create_ctx(CSLUL_P_IFACE); TEST_SOURCE("since 1.2\n" "func Thing.f() -> int\n", 1); ver12 = (struct ApiDef *)lookup(ctx, ctx->module.apidefs_root, "1.2"); t = lookup_type(ctx, "Thing"); tassert(t != NULL); tassert(t->typeidents != NULL); f = (struct TopLevelIdent *)lookup(ctx, t->typeidents, "f"); tassert(f != NULL); tassert(f->sinceversions != NULL); tsoftassert(f->sinceversions->next == NULL); tsoftassert(f->sinceversions->version == ver12); tassert(f->decl.type.type == T_METHOD); free_ctx(ctx); } static void test_parse_method_parametric(void) { struct TypeDecl *t; struct Type *ft; struct IdentDecl *pf; struct CSlul *ctx = create_ctx(CSLUL_P_IMPL); TEST_SOURCE("func Thing.f() -> int { return 0 }\n", 1); t = lookup_type(ctx, "Thing"); tassert(t != NULL); tassert(t->typeidents != NULL); pf = (struct IdentDecl *)lookup(ctx, t->typeidents, "f"); tassert(pf != NULL); tassert(pf->type.type == T_GENERICDEF); tassert(pf->type.u.gdef != NULL); tassert(lookup(ctx, pf->type.u.gdef->params_root, "T") != NULL); ft = &pf->type.u.gdef->basetype; tassert(ft->type == T_METHOD); tassert(ft->misc == 0); free_ctx(ctx); } static void test_parse_method_parametric_versioned(void) { struct TypeDecl *t; struct Type *ft; struct TopLevelIdent *f; struct ApiDef *ver12; struct CSlul *ctx = create_ctx(CSLUL_P_IFACE); TEST_SOURCE("since 1.2\n" "func Thing.f() -> int\n", 1); ver12 = (struct ApiDef *)lookup(ctx, ctx->module.apidefs_root, "1.2"); t = lookup_type(ctx, "Thing"); tassert(t != NULL); tassert(t->typeidents != NULL); f = (struct TopLevelIdent *)lookup(ctx, t->typeidents, "f"); tassert(f != NULL); tassert(f->sinceversions != NULL); tsoftassert(f->sinceversions->next == NULL); tsoftassert(f->sinceversions->version == ver12); tassert(f->decl.type.type == T_GENERICDEF); tassert(f->decl.type.u.gdef != NULL); tassert(lookup(ctx, f->decl.type.u.gdef->params_root, "T") != NULL); ft = &f->decl.type.u.gdef->basetype; tassert(ft->type == T_METHOD); tassert(ft->misc == 0); free_ctx(ctx); } #if 0 static void test_parse_method_classparam(void) { struct CSlul *ctx = create_ctx(CSLUL_P_IFACE); /* TODO decide on syntax here func List.add!(ref T elem) func List.add!(ref T elem) func List.add!(ref T elem) func List.add!(ref $T elem) # special syntax for type parameters func List.add!(ref %T elem) func List.add!(ref @T elem) func List.add!(ref .T elem) - with this syntax, the definition syntax should perhaps be changed to match? type List = ... - perhaps not necessary, as long as error messages are good enough func List.add@(ref T elem) # "@" for arena access? type T = typeparam func List.add!(ref T elem) type List = extern func List.add!(ref T elem) - the problem with this is that the functions might be in different files, and there can only be one definition of List - allow multiple extern definitions? - the advantage of this solution, is that the code will be very simple in perhaps 99% of the cases. For the other cases, the error messages should explain how to solve the problem (this could be triggered if a missing type is used in a class context (can this be detected?) and exists in the class. (Or perhaps if a type is named T, and appears in class context, and does not exist) func List.add!(ref T elem) - this coud work if we add ALL parameters to the "classparams" tree (except those that are per-function type params), and then resolve them during verification, when the function is first seen. - but this requires two identifier lookup phases! */ TEST_SOURCE("type Thing = extern\n" "func Thing.f(ref T t) -> int\n", 1); free_ctx(ctx); } #endif /* TODO tests of: - parametric class - qualifiers (! and threaded/aliased/writeonly) - ref type ("own", maybe "arena") - should "!" imply arena access? - or use "!" for var access, and "@" or "!!" for var+arena access? - or "!" for var, "@" for arena, and "!@" for arena? - or "!var", "!arena", and "!var!arena" (or !var,arena or !var,!arena) - or just "f var()", "f arena()" - which syntax to use for ownership-taking methods? f@(), f@~(), ~f@(), f@@(), f~(), ...? */ static void test_parse_method_badeof1(void) { struct CSlul *ctx = create_ctx(CSLUL_P_IFACE); ctx->parsed_module->is_unstable = 1; expect_error(CSLUL_E_IDENTIFIEREXPECTED, 1, 11); TEST_SOURCE("func Test.\n", 1); free_ctx(ctx); } static void test_parse_method_badeof2(void) { struct CSlul *ctx = create_ctx(CSLUL_P_IFACE); ctx->parsed_module->is_unstable = 1; expect_error(CSLUL_E_UNEXPECTEDEOF, 1, 12); TEST_SOURCE("func Test.f\n", 1); free_ctx(ctx); } static void test_parse_method_badsymbol(void) { struct CSlul *ctx = create_ctx(CSLUL_P_IFACE); ctx->parsed_module->is_unstable = 1; expect_error(CSLUL_E_IDENTIFIEREXPECTED, 1, 11); TEST_SOURCE("func Test.if(", 0); /* If any operators that start with . are added, then we should test one of those also */ free_ctx(ctx); } static void test_parse_method_dupl(void) { struct CSlul *ctx = create_ctx(CSLUL_P_IMPL); expect_error(CSLUL_E_IDENTEXISTS, 2, 11); TEST_SOURCE("func Test.f() -> int { return 1 }\n" "func Test.f() -> int { return 1 }\n", 1); free_ctx(ctx); } static void test_parse_method_dupl_ver(void) { struct CSlul *ctx = create_ctx(CSLUL_P_IFACE); expect_error(CSLUL_E_IDENTEXISTS, 4, 11); TEST_SOURCE("since 1.0\n" "func Test.f() -> int\n" "since 1.1\n" "func Test.f() -> int\n", 1); free_ctx(ctx); } static void check_null_initval(struct CSlul *ctx) { struct IdentDecl *decl; tassert(ctx->tl.idents_root); decl = (struct IdentDecl*)ctx->tl.idents_root; tassert(decl->u.initval != NULL); tsoftassert(decl->u.initval->rpn == NULL); tsoftassert(decl->u.initval->root == NULL); } static void test_parse_expr_empty1(void) { struct CSlul *ctx = create_ctx(CSLUL_P_IMPL); expect_error(CSLUL_E_UNEXPECTEDEOF, 1, 13); TEST_SOURCE("data int i =\n", 1); check_null_initval(ctx); free_ctx(ctx); } static void test_parse_expr_empty2(void) { struct CSlul *ctx = create_ctx(CSLUL_P_IMPL); expect_error(CSLUL_E_UNEXPECTEDEOF, 1, 13); TEST_SOURCE("data int i = #\n", 1); check_null_initval(ctx); free_ctx(ctx); } static void test_parse_expr_bad_eof(void) { struct CSlul *ctx = create_ctx(CSLUL_P_IMPL); expect_error(CSLUL_E_UNEXPECTEDEOF, 1, 16); TEST_SOURCE("data int i = 1+\n", 1); check_null_initval(ctx); free_ctx(ctx); } static void test_parse_expr_terminal(void) { struct CSlul *ctx = create_ctx(CSLUL_P_IMPL); struct IdentDecl *decl; struct ExprNode *rpn, *expr; TEST_SOURCE("data int i = 1\n", 1); tassert(ctx->tl.idents_root); decl = (struct IdentDecl*)ctx->tl.idents_root; tassert(decl->u.initval != NULL); tassert(decl->u.initval->rpn != NULL); rpn = decl->u.initval->rpn; ASSERT_RPN_INTEGER(1); ASSERT_RPN_END; expr = decl->u.initval->root; tassert(expr != NULL); tassert(expr->exprtype == E_INTEGER); tsoftassert(expr->a.intval == 1); ASSERT_LINECOL(&decl->ident, 1, 10); free_ctx(ctx); } static void test_parse_expr_typeident(void) { struct CSlul *ctx = create_ctx(CSLUL_P_IMPL); struct IdentDecl *decl; TEST_SOURCE("data Thing i = .typeident\n", 1); tassert(ctx->tl.idents_root); decl = (struct IdentDecl*)ctx->tl.idents_root; tassert(decl->u.initval != NULL); ASSERT_EXPR_TYPEIDENT(decl->u.initval, "typeident"); free_ctx(ctx); } static void test_parse_expr_field(void) { struct CSlul *ctx = create_ctx(CSLUL_P_IMPL); struct IdentDecl *decl; struct ExprNode *rpn, *expr; TEST_SOURCE("data int i = obj.field\n", 1); tassert(ctx->tl.idents_root); decl = (struct IdentDecl*)ctx->tl.idents_root; tassert(decl->u.initval != NULL); tassert(decl->u.initval->rpn != NULL); rpn = decl->u.initval->rpn; ASSERT_RPN_IDENT("obj"); ASSERT_RPN_FIELD("field"); ASSERT_RPN_END; expr = decl->u.initval->root; tassert(expr != NULL); tassert(expr->exprtype == E_FIELD); free_ctx(ctx); } static void test_parse_expr_bad_dot(void) { struct CSlul *ctx = create_ctx(CSLUL_P_IMPL); expect_error(CSLUL_E_UNEXPECTEDEOF, 1, 15); TEST_SOURCE("data int i = .\n", 1); check_null_initval(ctx); free_ctx(ctx); } static void test_parse_expr_bad_exclamation(void) { struct CSlul *ctx = create_ctx(CSLUL_P_IMPL); expect_error(CSLUL_E_BADEXPRTOKEN, 1, 16); TEST_SOURCE("data int i = 1 ! 2\n", 1); check_null_initval(ctx); free_ctx(ctx); } static void test_parse_expr_prefix(void) { struct CSlul *ctx = create_ctx(CSLUL_P_IMPL); struct IdentDecl *decl; struct ExprNode *rpn, *expr; TEST_SOURCE("data int i = -1\n", 1); tassert(ctx->tl.idents_root); decl = (struct IdentDecl*)ctx->tl.idents_root; tassert(decl->u.initval != NULL); tassert(decl->u.initval->rpn != NULL); rpn = decl->u.initval->rpn; ASSERT_RPN_INTEGER(1); ASSERT_RPN(CSLUL_T_Minus, RC_UNARY); ASSERT_RPN_END; expr = decl->u.initval->root; tassert(expr != NULL); tassert(expr->exprtype == E_UNARYOP); tassert(expr->op == OP_NEG); tassert(expr->a.expr != NULL); tassert(expr->a.expr->exprtype == E_INTEGER); tsoftassert(expr->a.expr->a.intval == 1); free_ctx(ctx); } static void test_parse_expr_prefix_nested(void) { struct CSlul *ctx = create_ctx(CSLUL_P_IMPL); struct IdentDecl *decl; struct ExprNode *rpn, *expr; TEST_SOURCE("data int i = -(-1 + -2)\n", 1); tassert(ctx->tl.idents_root); decl = (struct IdentDecl*)ctx->tl.idents_root; tassert(decl->u.initval != NULL); tassert(decl->u.initval->rpn != NULL); rpn = decl->u.initval->rpn; ASSERT_RPN_INTEGER(1); ASSERT_RPN(CSLUL_T_Minus, RC_UNARY); ASSERT_RPN_INTEGER(2); ASSERT_RPN(CSLUL_T_Minus, RC_UNARY); ASSERT_RPN(CSLUL_T_Plus, RC_OP); ASSERT_RPN(CSLUL_T_Minus, RC_UNARY); ASSERT_RPN_END; expr = decl->u.initval->root; tassert(expr->exprtype == E_UNARYOP); tassert(expr->op == OP_NEG); tassert(expr->a.expr != NULL); expr = expr->a.expr; tassert(expr != NULL); tassert(expr->exprtype == E_BINARYOP); tassert(expr->op == OP_ADD); tassert(expr->a.expr != NULL); tassert(expr->a.expr->exprtype == E_UNARYOP); tassert(expr->a.expr->op == OP_NEG); tassert(expr->a.expr->a.expr != NULL); tassert(expr->a.expr->a.expr->exprtype == E_INTEGER); tsoftassert(expr->a.expr->a.expr->a.intval == 1); tassert(expr->b.expr != NULL); tassert(expr->b.expr->exprtype == E_UNARYOP); tassert(expr->b.expr->op == OP_NEG); tassert(expr->b.expr->a.expr != NULL); tassert(expr->b.expr->a.expr->exprtype == E_INTEGER); tsoftassert(expr->b.expr->a.expr->a.intval == 2); free_ctx(ctx); } static void test_parse_expr_prefix_unexpected(void) { struct CSlul *ctx = create_ctx(CSLUL_P_IMPL); expect_error(CSLUL_E_OPERATOREXPECTED, 1, 15); TEST_SOURCE("data int i = 1 not + 2\n", 1); check_null_initval(ctx); free_ctx(ctx); } static void test_parse_expr_binary_nested(void) { struct CSlul *ctx = create_ctx(CSLUL_P_IMPL); struct IdentDecl *decl; struct ExprNode *rpn, *expr, *a, *b, *c; /* ---a--- --b-- */ TEST_SOURCE("data int i = 1 + 2*3 + 45/6\n", 1); /* -c- */ tassert(ctx->tl.idents_root); decl = (struct IdentDecl*)ctx->tl.idents_root; tassert(decl->u.initval != NULL); tassert(decl->u.initval->rpn != NULL); rpn = decl->u.initval->rpn; ASSERT_RPN_INTEGER(1); ASSERT_RPN_INTEGER(2); ASSERT_RPN_INTEGER(3); ASSERT_RPN(CSLUL_T_Asterisk, RC_OP); ASSERT_RPN(CSLUL_T_Plus, RC_OP); ASSERT_RPN_INTEGER(45); ASSERT_RPN_INTEGER(6); ASSERT_RPN(CSLUL_T_Slash, RC_OP); ASSERT_RPN(CSLUL_T_Plus, RC_OP); ASSERT_RPN_END; expr = decl->u.initval->root; tassert(expr->exprtype == E_BINARYOP); tassert(expr->op == OP_ADD); tassert((a = expr->a.expr) != NULL); tassert(a->exprtype == E_BINARYOP); tassert(a->op == OP_ADD); tassert(a->a.expr != NULL); tassert(a->a.expr->exprtype == E_INTEGER); tsoftassert(a->a.expr->a.intval == 1); tassert((c = a->b.expr) != NULL); tassert(c->exprtype == E_BINARYOP); tassert(c->op == OP_MUL); tassert(c->a.expr != NULL); tassert(c->a.expr->exprtype == E_INTEGER); tsoftassert(c->a.expr->a.intval == 2); tassert(c->b.expr != NULL); tassert(c->b.expr->exprtype == E_INTEGER); tsoftassert(c->b.expr->a.intval == 3); tassert((b = expr->b.expr) != NULL); tassert(b->exprtype == E_BINARYOP); tassert(b->op == OP_DIV); tassert(b->a.expr != NULL); tassert(b->a.expr->exprtype == E_INTEGER); tsoftassert(b->a.expr->a.intval == 45); tassert(b->b.expr != NULL); tassert(b->b.expr->exprtype == E_INTEGER); tsoftassert(b->b.expr->a.intval == 6); free_ctx(ctx); } static void test_parse_expr_binary_parens(void) { struct CSlul *ctx = create_ctx(CSLUL_P_IMPL); struct IdentDecl *decl; struct ExprNode *rpn, *expr, *a, *b, *c; /* --a-- ------b----- */ TEST_SOURCE("data int i = (1+2) * ((3+45) / 6)\n", 1); /* --c--- */ tassert(ctx->tl.idents_root); decl = (struct IdentDecl*)ctx->tl.idents_root; tassert(decl->u.initval != NULL); tassert(decl->u.initval->rpn != NULL); rpn = decl->u.initval->rpn; ASSERT_RPN_INTEGER(1); ASSERT_RPN_INTEGER(2); ASSERT_RPN(CSLUL_T_Plus, RC_OP); ASSERT_RPN_INTEGER(3); ASSERT_RPN_INTEGER(45); ASSERT_RPN(CSLUL_T_Plus, RC_OP); ASSERT_RPN_INTEGER(6); ASSERT_RPN(CSLUL_T_Slash, RC_OP); ASSERT_RPN(CSLUL_T_Asterisk, RC_OP); ASSERT_RPN_END; expr = decl->u.initval->root; tassert(expr->exprtype == E_BINARYOP); tassert(expr->op == OP_MUL); tassert((a = expr->a.expr) != NULL); tassert(a->exprtype == E_BINARYOP); tassert(a->op == OP_ADD); tassert(a->a.expr != NULL); tassert(a->a.expr->exprtype == E_INTEGER); tsoftassert(a->a.expr->a.intval == 1); tassert(a->b.expr != NULL); tassert(a->b.expr->exprtype == E_INTEGER); tsoftassert(a->b.expr->a.intval == 2); tassert((b = expr->b.expr) != NULL); tassert(b->exprtype == E_BINARYOP); tassert(b->op == OP_DIV); tassert(b->a.expr != NULL); tassert((c = b->a.expr) != NULL); tassert(c->exprtype == E_BINARYOP); tassert(c->op == OP_ADD); tassert(c->a.expr != NULL); tassert(c->a.expr->exprtype == E_INTEGER); tsoftassert(c->a.expr->a.intval == 3); tassert(c->b.expr != NULL); tassert(c->b.expr->exprtype == E_INTEGER); tsoftassert(c->b.expr->a.intval == 45); tassert(b->b.expr != NULL); tassert(b->b.expr->exprtype == E_INTEGER); tsoftassert(b->b.expr->a.intval == 6); free_ctx(ctx); } #define ASSERT_EXPR_ELEM(value) do { \ struct ExprNode *elemexpr = *elemptr; \ tassert(elemexpr != NULL); \ tassert(elemexpr->exprtype == E_INTEGER); \ tassert(elemexpr->a.intval == (value)); \ elemptr++; \ } while (0) static void test_parse_expr_array(void) { struct CSlul *ctx = create_ctx(CSLUL_P_IMPL); struct IdentDecl *i; struct ExprNode *rpn, *expr, *arr, **elemptr; TEST_SOURCE("data int i = [12, 34, 56][2]\n", 1); i = lookup_toplevel(ctx, "i"); tassert(i != NULL); tassert(i->u.initval != NULL); tassert(i->u.initval->rpn != NULL); rpn = i->u.initval->rpn; ASSERT_RPN_INTEGER(12); ASSERT_RPN_INTEGER(34); ASSERT_RPN_INTEGER(56); ASSERT_RPN(CSLUL_T_LSquare, RC_ELEMLIST); ASSERT_RPN_INTEGER(2); ASSERT_RPN(CSLUL_T_LSquare, RC_ARGLIST); ASSERT_RPN_END; tassert((expr = i->u.initval->root) != NULL); tassert(expr->exprtype == E_INDEX); tassert(expr->a.exprlist != NULL); tassert(expr->a.exprlist->length == 1); elemptr = expr->a.exprlist->elems; ASSERT_EXPR_ELEM(2); tassert((arr = expr->b.expr) != NULL); tassert(arr->exprtype == E_ARRAY); tassert(arr->a.exprlist != NULL); tassert(arr->a.exprlist->length == 3); elemptr = arr->a.exprlist->elems; ASSERT_EXPR_ELEM(12); ASSERT_EXPR_ELEM(34); ASSERT_EXPR_ELEM(56); free_ctx(ctx); } static void test_parse_expr_array_nested(void) { struct CSlul *ctx = create_ctx(CSLUL_P_IMPL); struct IdentDecl *i; struct ExprNode *rpn, *expr, *outer, **oelemptr, *inner, **elemptr; TEST_SOURCE("data int i = [[11,12], [21,22], [31,32]][2,1]\n", 1); i = lookup_toplevel(ctx, "i"); tassert(i != NULL); tassert(i->u.initval != NULL); tassert(i->u.initval->rpn != NULL); rpn = i->u.initval->rpn; ASSERT_RPN_INTEGER(11); ASSERT_RPN_INTEGER(12); ASSERT_RPN(CSLUL_T_LSquare, RC_ELEMLIST); ASSERT_RPN_INTEGER(21); ASSERT_RPN_INTEGER(22); ASSERT_RPN(CSLUL_T_LSquare, RC_ELEMLIST); ASSERT_RPN_INTEGER(31); ASSERT_RPN_INTEGER(32); ASSERT_RPN(CSLUL_T_LSquare, RC_ELEMLIST); ASSERT_RPN(CSLUL_T_LSquare, RC_ELEMLIST); ASSERT_RPN_INTEGER(2); ASSERT_RPN_INTEGER(1); ASSERT_RPN(CSLUL_T_LSquare, RC_ARGLIST); ASSERT_RPN_END; tassert((expr = i->u.initval->root) != NULL); tassert(expr->exprtype == E_INDEX); tassert(expr->a.exprlist != NULL); tassert(expr->a.exprlist->length == 2); elemptr = expr->a.exprlist->elems; ASSERT_EXPR_ELEM(2); ASSERT_EXPR_ELEM(1); tassert((outer = expr->b.expr) != NULL); tassert(outer->exprtype == E_ARRAY); tassert(outer->a.exprlist != NULL); tassert(outer->a.exprlist->length == 3); oelemptr = outer->a.exprlist->elems; /* First inner array: [11,12] */ tassert((inner = *(oelemptr++)) != NULL); tassert(inner->exprtype == E_ARRAY); tassert(inner->a.exprlist != NULL); tassert(inner->a.exprlist->length == 2); elemptr = inner->a.exprlist->elems; ASSERT_EXPR_ELEM(11); ASSERT_EXPR_ELEM(12); /* Second inner array: [21,22] */ tassert((inner = *(oelemptr++)) != NULL); tassert(inner->exprtype == E_ARRAY); tassert(inner->a.exprlist != NULL); tassert(inner->a.exprlist->length == 2); elemptr = inner->a.exprlist->elems; ASSERT_EXPR_ELEM(21); ASSERT_EXPR_ELEM(22); /* Third inner array: [31,32] */ tassert((inner = *(oelemptr++)) != NULL); tassert(inner->exprtype == E_ARRAY); tassert(inner->a.exprlist != NULL); tassert(inner->a.exprlist->length == 2); elemptr = inner->a.exprlist->elems; ASSERT_EXPR_ELEM(31); ASSERT_EXPR_ELEM(32); free_ctx(ctx); } static void test_parse_expr_array_trailingcomma(void) { struct CSlul *ctx = create_ctx(CSLUL_P_IMPL); struct IdentDecl *ident; struct ExprNode *rpn, *expr, **elemptr; TEST_SOURCE("data [1]bool arr = [1,]\n", 1); ident = lookup_toplevel(ctx, "arr"); tassert(ident != NULL); tassert(ident->u.initval != NULL); tassert(ident->u.initval->rpn != NULL); rpn = ident->u.initval->rpn; ASSERT_RPN_INTEGER(1); ASSERT_RPN(CSLUL_T_LSquare, RC_ELEMLIST); ASSERT_RPN_END; tassert((expr = ident->u.initval->root) != NULL); tassert(expr->exprtype == E_ARRAY); tassert(expr->a.exprlist != NULL); tassert(expr->a.exprlist->length == 1); elemptr = expr->a.exprlist->elems; ASSERT_EXPR_ELEM(1); free_ctx(ctx); } static void test_parse_expr_array_noend(void) { struct CSlul *ctx = create_ctx(CSLUL_P_IMPL); expect_error(CSLUL_E_INCOMPLETEEXPR, 1, 21); TEST_SOURCE("data [1]byte arr = [\n" "data int i = 123\n", 1); free_ctx(ctx); } static void test_parse_expr_array_badeof(void) { struct CSlul *ctx = create_ctx(CSLUL_P_IMPL); expect_error(CSLUL_E_UNEXPECTEDEOF, 1, 21); TEST_SOURCE("data [1]byte arr = [\n", 1); free_ctx(ctx); } static void test_parse_expr_string_empty(void) { struct CSlul *ctx = create_ctx(CSLUL_P_IMPL); struct IdentDecl *ident; struct ExprNode *rpn; struct StringLiteral *str; TEST_SOURCE("data string s = \"\"\n", 1); ident = lookup_toplevel(ctx, "s"); tassert(ident != NULL); tassert(ident->u.initval != NULL); tassert(ident->u.initval->rpn != NULL); rpn = ident->u.initval->rpn; tassert(rpn->exprtype == E_STRING); tassert(rpn->a.strval != NULL); str = rpn->a.strval; tassert(str->data != NULL); tsoftassert(str->data[0] == '\0'); tassert(str->u.length == 0); tsoftassert(rpn->rpnnext == NULL); free_ctx(ctx); } static void test_parse_expr_string_single(void) { struct CSlul *ctx = create_ctx(CSLUL_P_IMPL); struct IdentDecl *ident; struct ExprNode *rpn; struct StringLiteral *str; TEST_SOURCE("data string s = \"abc\"\n", 1); ident = lookup_toplevel(ctx, "s"); tassert(ident != NULL); tassert(ident->u.initval != NULL); tassert(ident->u.initval->rpn != NULL); rpn = ident->u.initval->rpn; tassert(rpn->exprtype == E_STRING); tassert(rpn->a.strval != NULL); str = rpn->a.strval; tassert(str->data != NULL); tassert(str->u.length == 3); tsoftassert(!memcmp(str->data, "abc", 3)); tsoftassert(rpn->rpnnext == NULL); free_ctx(ctx); } #define ASSERT_CHUNK(c, s) do { \ tassert((c) != NULL); \ tassert((c)->length == sizeof(s)-1); \ tassert((c)->data != NULL); \ tassert(!memcmp((c)->data, (s), sizeof(s)-1)); \ } while (0) static void test_parse_expr_string_2chunks(void) { struct CSlul *ctx = create_ctx(CSLUL_P_IMPL); struct IdentDecl *ident; struct ExprNode *rpn; struct StringLiteral *str; struct StringChunk *chunk; TEST_SOURCE("data string s =\n" " \"abc\"\n" " \"defg\"\n", 1); ident = lookup_toplevel(ctx, "s"); tassert(ident != NULL); tassert(ident->u.initval != NULL); tassert(ident->u.initval->rpn != NULL); rpn = ident->u.initval->rpn; tassert(rpn->exprtype == E_STRING); tassert(rpn->a.strval != NULL); str = rpn->a.strval; tassert(str->data == NULL); tassert(str->u.chunks != NULL); chunk = str->u.chunks; ASSERT_CHUNK(chunk, "abc"); chunk = chunk->next; ASSERT_CHUNK(chunk, "defg"); tsoftassert(chunk->next == NULL); tsoftassert(rpn->rpnnext == NULL); free_ctx(ctx); } static void test_parse_expr_string_5chunks(void) { struct CSlul *ctx = create_ctx(CSLUL_P_IMPL); struct IdentDecl *ident; struct ExprNode *rpn; struct StringLiteral *str; struct StringChunk *chunk; TEST_SOURCE("data string s =\n" " \"abc\"\n" " \"\\x00test\"\n" " \"XYZ\"\n" " \"\"\n" " \"test\\\\\"\n", 1); ident = lookup_toplevel(ctx, "s"); tassert(ident != NULL); tassert(ident->u.initval != NULL); tassert(ident->u.initval->rpn != NULL); rpn = ident->u.initval->rpn; tassert(rpn->exprtype == E_STRING); tassert(rpn->a.strval != NULL); str = rpn->a.strval; tassert(str->data == NULL); tassert(str->u.chunks != NULL); chunk = str->u.chunks; ASSERT_CHUNK(chunk, "abc"); chunk = chunk->next; ASSERT_CHUNK(chunk, "\0test"); chunk = chunk->next; ASSERT_CHUNK(chunk, "XYZ"); chunk = chunk->next; ASSERT_CHUNK(chunk, ""); chunk = chunk->next; ASSERT_CHUNK(chunk, "test\\"); tsoftassert(chunk->next == NULL); tsoftassert(rpn->rpnnext == NULL); free_ctx(ctx); } static void test_parse_expr_ambiguous_mix(void) { struct CSlul *ctx = create_ctx(CSLUL_P_IMPL); expect_error(CSLUL_E_AMBIGUOUSOPERMIX, 1, 29); TEST_SOURCE("data bool b = 1==2 and 3==4 or 5==6\n", 1); free_ctx(ctx); } static void test_parse_expr_unclosed_paren(void) { struct CSlul *ctx = create_ctx(CSLUL_P_IMPL); expect_error(CSLUL_E_UNEXPECTEDEOF, 1, 22); expect_error(CSLUL_E_UNCLOSEDPAREN, 1, 22); TEST_SOURCE("data bool b = (1 == 2\n", 1); free_ctx(ctx); } static void test_parse_expr_mismatched_paren1(void) { struct CSlul *ctx = create_ctx(CSLUL_P_IMPL); expect_error(CSLUL_E_WRONGPARENTYPE, 1, 22); TEST_SOURCE("data bool b = (1 == 2]\n", 1); free_ctx(ctx); } static void test_parse_expr_mismatched_paren2(void) { struct CSlul *ctx = create_ctx(CSLUL_P_IMPL); expect_error(CSLUL_E_WRONGPARENTYPE, 1, 21); TEST_SOURCE("data [1]int arr = [1)\n", 1); free_ctx(ctx); } static void test_parse_expr_mismatched_paren3(void) { struct CSlul *ctx = create_ctx(CSLUL_P_IMPL); expect_error(CSLUL_E_WRONGPARENTYPE, 1, 21); expect_error(CSLUL_E_MISSINGOPERAND, 1, 20); TEST_SOURCE("data [1]int arr = [,)\n", 1); free_ctx(ctx); } static void test_parse_expr_unexpected_operator(void) { struct CSlul *ctx = create_ctx(CSLUL_P_IMPL); expect_error(CSLUL_E_OPERATORNOTEXP, 1, 14); TEST_SOURCE("data int b = * 1\n", 1); free_ctx(ctx); } static void test_parse_expr_missing_operand1(void) { struct CSlul *ctx = create_ctx(CSLUL_P_IMPL); expect_error(CSLUL_E_MISSINGOPERAND, 1, 18); TEST_SOURCE("data int b = (1 + )\n", 1); free_ctx(ctx); } static void test_parse_expr_missing_operand2(void) { struct CSlul *ctx = create_ctx(CSLUL_P_IMPL); expect_error(CSLUL_E_MISSINGOPERAND, 1, 15); TEST_SOURCE("data int b = (,)\n", 1); free_ctx(ctx); } static void test_parse_expr_missing_operand3(void) { struct CSlul *ctx = create_ctx(CSLUL_P_IMPL); expect_error(CSLUL_E_UNEXPECTEDEOF, 1, 18); /* FIXME avoid this error */ expect_error(CSLUL_E_MISSINGOPERAND, 1, 15); TEST_SOURCE("data int b = (,)-\n", 1); free_ctx(ctx); } static void test_parse_expr_missing_operand4(void) { struct CSlul *ctx = create_ctx(CSLUL_P_IMPL); expect_error(CSLUL_E_MISSINGOPERAND, 1, 16); TEST_SOURCE("data int b = [(,).x\n", 1); free_ctx(ctx); } static void test_parse_expr_missing_operand5(void) { struct CSlul *ctx = create_ctx(CSLUL_P_IMPL); expect_error(CSLUL_E_MISSINGOPERAND, 1, 15); TEST_SOURCE("data int b = (,)-x\n", 1); free_ctx(ctx); } static void test_parse_expr_missing_operand6(void) { struct CSlul *ctx = create_ctx(CSLUL_P_IMPL); expect_error(CSLUL_E_MISSINGOPERAND, 1, 16); expect_error(CSLUL_E_MISSINGOPERAND, 1, 15); TEST_SOURCE("data int b = (,,)-x\n", 1); free_ctx(ctx); } static void test_parse_expr_missing_operand7(void) { struct CSlul *ctx = create_ctx(CSLUL_P_IMPL); expect_error(CSLUL_E_TRAILINGCOMMASTRUCT, 1, 18); expect_error(CSLUL_E_MISSINGOPERAND, 1, 17); TEST_SOURCE("data int b = (1,,)-x\n", 1); free_ctx(ctx); } static void test_parse_expr_missing_operator(void) { struct CSlul *ctx = create_ctx(CSLUL_P_IMPL); expect_error(CSLUL_E_OPERATOREXPECTED, 1, 16); TEST_SOURCE("data bool b = 1 2\n", 1); free_ctx(ctx); } static void test_parse_expr_bad_token1(void) { struct CSlul *ctx = create_ctx(CSLUL_P_IMPL); expect_error(CSLUL_E_BADEXPRTOKEN, 1, 17); TEST_SOURCE("data bool b = 1 if\n", 1); free_ctx(ctx); } static void test_parse_expr_bad_token2(void) { struct CSlul *ctx = create_ctx(CSLUL_P_IMPL); expect_error(CSLUL_E_TOKENAFTEROPERATOR, 1, 19); TEST_SOURCE("data bool b = 1 + if\n", 1); free_ctx(ctx); } static void test_parse_expr_bad_semicolon(void) { struct CSlul *ctx = create_ctx(CSLUL_P_IMPL); expect_error(CSLUL_E_BADSEMICOLON, 1, 16); TEST_SOURCE("data bool b = 1;\n", 1); free_ctx(ctx); } /* TODO test skip-to-end in exprs when it is implemented */ /** Checks that the opinfo array correctly maps tokens to operators */ static void test_parse_opinfo(void) { tsoftassert(opinfos[2*CSLUL_T_Plus].op == OP_ADD); tsoftassert(opinfos[2*CSLUL_T_Plus+1].op == OP_POS); tsoftassert(opinfos[2*CSLUL_T_Minus].op == OP_SUB); tsoftassert(opinfos[2*CSLUL_T_Minus+1].op == OP_NEG); tsoftassert(opinfos[2*CSLUL_T_Asterisk].op == OP_MUL); tsoftassert(opinfos[2*CSLUL_T_Slash].op == OP_DIV); tsoftassert(opinfos[2*CSLUL_T_Less].op == OP_LESS); tsoftassert(opinfos[2*CSLUL_T_Assign].op == OP_ASSIGN); tsoftassert(opinfos[2*CSLUL_T_Greater].op == OP_GREATER); tsoftassert(opinfos[2*CSLUL_T_Dot+1].op == OP_MEMBER); tsoftassert(opinfos[2*CSLUL_T_Question+1].op == OP_OPTIONAL); tsoftassert(opinfos[2*CSLUL_T_PlusAssign].op == OP_ADDASSIGN); tsoftassert(opinfos[2*CSLUL_T_MinusAssign].op == OP_SUBASSIGN); tsoftassert(opinfos[2*CSLUL_T_MultiplyAssign].op == OP_MULASSIGN); tsoftassert(opinfos[2*CSLUL_T_DivideAssign].op == OP_DIVASSIGN); tsoftassert(opinfos[2*CSLUL_T_LessEqual].op == OP_LEQ); tsoftassert(opinfos[2*CSLUL_T_Equal].op == OP_EQ); tsoftassert(opinfos[2*CSLUL_T_GreaterEqual].op == OP_GEQ); tsoftassert(opinfos[2*CSLUL_T_NotEqual].op == OP_NOTEQ); tsoftassert(opinfos[2*CSLUL_T_KW_Not+1].op == OP_NOT); tsoftassert(opinfos[2*CSLUL_T_KW_And].op == OP_AND); tsoftassert(opinfos[2*CSLUL_T_KW_Or].op == OP_OR); } static void assert_voidfunc(struct IdentDecl *f) { tassert(f != NULL); tassert(f->type.type == T_FUNC); tsoftassert(f->type.quals == IMPLQUALS); tsoftassert(f->type.misc == M_VOIDRETURN); tassert(f->type.u.func != NULL); tsoftassert(f->type.u.func->returntype.type == T_INTERNAL); tsoftassert(f->type.u.func->returntype.u.internal == IT_Void); tsoftassert(f->type.u.func->params.first == NULL); tsoftassert(f->type.u.func->params.fields_root == NULL); tsoftassert(f->type.u.func->params.count == 0); tassert(f->u.funcbody != NULL); } static void assert_intfunc(struct IdentDecl *f) { tassert(f != NULL); tassert(f->type.type == T_FUNC); tsoftassert(f->type.quals == IMPLQUALS); tsoftassert(f->type.misc == 0); tassert(f->type.u.func != NULL); ASSERT_TYPE_INT(&f->type.u.func->returntype); tsoftassert(f->type.u.func->params.first == NULL); tsoftassert(f->type.u.func->params.fields_root == NULL); tsoftassert(f->type.u.func->params.count == 0); tassert(f->u.funcbody != NULL); } static void free_assert(struct CSlul *ctx) { tsoftassert(ctx->typedepth == -1); tsoftassert(ctx->exprdepth == -1); free_ctx(ctx); } static void test_parse_funcbody_empty(void) { struct CSlul *ctx = create_ctx(CSLUL_P_IMPL); struct IdentDecl *f; struct FuncBody *fb; TEST_SOURCE("func f() {}\n", 1); f = lookup_toplevel(ctx, "f"); assert_voidfunc(f); ASSERT_LINECOL(&f->ident, 1, 6); tassert((fb = f->u.funcbody) != NULL); tsoftassert(fb->stmtblock.base == NULL); tsoftassert(fb->stmtblock.stmt.type == S_NOP); tsoftassert(fb->stmtblock.stmt.next == NULL); tsoftassert(fb->stmtblock.idents == NULL); tsoftassert(fb->next == NULL); free_assert(ctx); } static void test_parse_funcbody_expr1(void) { struct CSlul *ctx = create_ctx(CSLUL_P_IMPL); struct IdentDecl *f, *var_i; struct FuncBody *fb; struct Stmt *s; struct ExprNode *rpn; TEST_SOURCE("func f()\n" "{\n" " var int i\n" " i = 123\n" "}\n", 1); f = lookup_toplevel(ctx, "f"); assert_voidfunc(f); ASSERT_LINECOL(&f->ident, 1, 6); tassert((fb = f->u.funcbody) != NULL); tsoftassert(fb->stmtblock.base == NULL); tsoftassert(fb->stmtblock.idents != NULL); s = &fb->stmtblock.stmt; /* First statement: var int i */ ASSERT_STMT_DECL(s, "i"); tassert(s->u.vardef != NULL); var_i = &s->u.vardef->decl; ASSERT_LINECOL(&var_i->ident, 3, 13); tsoftassert(fb->stmtblock.idents == &var_i->ident); s = s->next; /* Second statement: i = 123 */ tassert(s != NULL); tassert(s->type == S_EXPR); tsoftassert(s->next == NULL); tassert(s->u.expr != NULL); tassert(s->u.expr->root != NULL); rpn = s->u.expr->rpn; ASSERT_RPN_IDENT("i"); tsoftassert(s->u.expr->rpn->a.ident == &var_i->ident); ASSERT_RPN_INTEGER(123); tsoftassert(rpn == s->u.expr->root); ASSERT_RPN(CSLUL_T_Assign, RC_OP); ASSERT_RPN_END; tsoftassert(s->u.expr->root->exprtype == E_BINARYOP); tsoftassert(s->u.expr->root->op == OP_ASSIGN); tsoftassert(fb->next == NULL); free_assert(ctx); } static void test_parse_funcbody_expr2(void) { struct CSlul *ctx = create_ctx(CSLUL_P_IMPL); struct IdentDecl *f, *g; struct FuncBody *fb; struct Stmt *s; struct ExprNode *rpn; TEST_SOURCE("func g() { }\n" "func f()\n" "{\n" " var int i\n" " i = 123\n" " i = 456\n" " g()\n" "}\n", 1); f = lookup_toplevel(ctx, "f"); g = lookup_toplevel(ctx, "g"); assert_voidfunc(f); assert_voidfunc(g); ASSERT_LINECOL(&f->ident, 2, 6); ASSERT_LINECOL(&g->ident, 1, 6); tassert((fb = f->u.funcbody) != NULL); tsoftassert(fb->stmtblock.base == NULL); tsoftassert(fb->stmtblock.idents != NULL); tsoftassert(fb->next == g->u.funcbody); tsoftassert(ctx->typedepth == -1); s = &fb->stmtblock.stmt; /* First statement: var int i */ ASSERT_STMT_DECL(s, "i"); s = s->next; /* Second statement: i = 123 */ tassert(s != NULL); tassert(s->type == S_EXPR); tsoftassert(s->next != NULL); tassert(s->u.expr != NULL); tassert(s->u.expr->root != NULL); rpn = s->u.expr->rpn; ASSERT_RPN_IDENT("i"); ASSERT_RPN_INTEGER(123); tsoftassert(rpn == s->u.expr->root); ASSERT_RPN(CSLUL_T_Assign, RC_OP); ASSERT_RPN_END; tsoftassert(s->u.expr->root->exprtype == E_BINARYOP); tsoftassert(s->u.expr->root->op == OP_ASSIGN); /* Third statement: i = 456 */ s = s->next; tassert(s != NULL); tassert(s->type == S_EXPR); tsoftassert(s->next != NULL); tassert(s->u.expr != NULL); tassert(s->u.expr->root != NULL); rpn = s->u.expr->rpn; ASSERT_RPN_IDENT("i"); ASSERT_RPN_INTEGER(456); tsoftassert(rpn == s->u.expr->root); ASSERT_RPN(CSLUL_T_Assign, RC_OP); ASSERT_RPN_END; tsoftassert(s->u.expr->root->exprtype == E_BINARYOP); tsoftassert(s->u.expr->root->op == OP_ASSIGN); /* Last statement: g() */ s = s->next; tassert(s != NULL); tassert(s->type == S_EXPR); tsoftassert(s->next == NULL); tassert(s->u.expr != NULL); tassert(s->u.expr->root != NULL); rpn = s->u.expr->rpn; ASSERT_RPN_IDENT("g"); tsoftassert(rpn == s->u.expr->root); ASSERT_RPN(CSLUL_T_LParen, RC_ARGLIST); ASSERT_RPN_END; tassert(s->u.expr->root->exprtype == E_CALL); tsoftassert(s->u.expr->root->a.exprlist != NULL); tsoftassert(s->u.expr->root->a.exprlist->length == 0); free_assert(ctx); } static void test_parse_funcbody_returnvoid(void) { struct CSlul *ctx = create_ctx(CSLUL_P_IMPL); struct IdentDecl *f; struct FuncBody *fb; struct Stmt *s; TEST_SOURCE("func f()\n" "{\n" " return\n" "}\n", 1); f = lookup_toplevel(ctx, "f"); assert_voidfunc(f); tassert((fb = f->u.funcbody) != NULL); tsoftassert(fb->stmtblock.base == NULL); tsoftassert(fb->stmtblock.idents == NULL); s = &fb->stmtblock.stmt; tassert(s->type == S_RETURN); tsoftassert(s->next == NULL); tsoftassert(s->u.expr == NULL); tsoftassert(fb->next == NULL); free_assert(ctx); } /** * Tests recovery with an expression with an error inside a type * inside a function body. */ static void test_parse_funcbody_recovery1(void) { struct CSlul *ctx = create_ctx(CSLUL_P_IMPL); /* TODO These errors could be improved */ /* The first [ starts an array type, which is missing the element type */ expect_error(CSLUL_E_BADTYPE, 4, 1); /* The double comma is interpreted as a missing operand in an array */ expect_error(CSLUL_E_MISSINGOPERAND, 3, 9); TEST_SOURCE("func f()\n" "{\n" " [[1,,2\n" "}\n", 1); free_assert(ctx); } static void test_parse_funcbody_returnexpr(void) { struct CSlul *ctx = create_ctx(CSLUL_P_IMPL); struct IdentDecl *f; struct FuncBody *fb; struct Stmt *s; TEST_SOURCE("func f() -> int\n" "{\n" " return 123\n" "}\n", 1); f = lookup_toplevel(ctx, "f"); assert_intfunc(f); tassert((fb = f->u.funcbody) != NULL); tsoftassert(fb->stmtblock.base == NULL); tsoftassert(fb->stmtblock.idents == NULL); s = &fb->stmtblock.stmt; ASSERT_STMT_RETURN_INT(s, 123); tsoftassert(fb->next == NULL); free_assert(ctx); } static void test_parse_funcbody_assert(void) { struct CSlul *ctx = create_ctx(CSLUL_P_IMPL); struct IdentDecl *f; struct FuncBody *fb; struct Stmt *s; TEST_SOURCE("func f()\n" "{\n" " assert 1 == 1\n" "}\n", 1); f = lookup_toplevel(ctx, "f"); assert_voidfunc(f); tassert((fb = f->u.funcbody) != NULL); tsoftassert(fb->stmtblock.base == NULL); tsoftassert(fb->stmtblock.idents == NULL); s = &fb->stmtblock.stmt; ASSERT_STMT_ASSERT_EQ(s, 1, 1); tsoftassert(s->next == NULL); tsoftassert(fb->next == NULL); free_assert(ctx); } static void test_parse_funcbody_block(void) { struct CSlul *ctx = create_ctx(CSLUL_P_IMPL); struct IdentDecl *f; struct FuncBody *fb; struct Stmt *s; TEST_SOURCE("func f()\n" "{\n" " {\n" " assert 1 == 1\n" " }\n" "}\n", 1); f = lookup_toplevel(ctx, "f"); assert_voidfunc(f); tassert((fb = f->u.funcbody) != NULL); tsoftassert(fb->stmtblock.base == NULL); tsoftassert(fb->stmtblock.idents == NULL); s = &fb->stmtblock.stmt; ASSERT_STMT_BLOCK(s); ASSERT_STMT_ASSERT_EQ(&s->u.block->stmt, 1, 1); tsoftassert(s->u.block->stmt.next == NULL); tsoftassert(s->next == NULL); tsoftassert(fb->next == NULL); free_assert(ctx); } static void test_parse_funcbody_if(void) { struct CSlul *ctx = create_ctx(CSLUL_P_IMPL); struct IdentDecl *f; struct FuncBody *fb; struct Stmt *s; TEST_SOURCE("func f()\n" "{\n" " if 0 == 1 return\n" " if 2 == 3 {\n" " if 4 == 5 { assert 1 == 1 }\n" " if 6 == 7 return\n" " }\n" " if 8 == 9 return\n" "}\n", 1); f = lookup_toplevel(ctx, "f"); assert_voidfunc(f); tassert((fb = f->u.funcbody) != NULL); tsoftassert(fb->stmtblock.base == NULL); tsoftassert(fb->stmtblock.idents == NULL); s = &fb->stmtblock.stmt; ASSERT_STMT_IF(s); ASSERT_EXPR_INT_EQ(s->u.ifstm->cond, 0, 1); ASSERT_STMT_RETURN(&s->u.ifstm->true_block.stmt); tsoftassert(s->u.ifstm->true_block.stmt.next == NULL); s = s->next; ASSERT_STMT_IF(s); ASSERT_EXPR_INT_EQ(s->u.ifstm->cond, 2, 3); { struct Stmt *s1 = &s->u.ifstm->true_block.stmt; ASSERT_STMT_IF(s1); ASSERT_EXPR_INT_EQ(s1->u.ifstm->cond, 4, 5); s1 = s1->next; ASSERT_STMT_IF(s1); ASSERT_EXPR_INT_EQ(s1->u.ifstm->cond, 6, 7); ASSERT_STMT_RETURN(&s1->u.ifstm->true_block.stmt); tsoftassert(s1->next == NULL); } s = s->next; ASSERT_STMT_IF(s); ASSERT_EXPR_INT_EQ(s->u.ifstm->cond, 8, 9); ASSERT_STMT_RETURN(&s->u.ifstm->true_block.stmt); tsoftassert(fb->next == NULL); free_assert(ctx); } static void test_parse_funcbody_else(void) { struct CSlul *ctx = create_ctx(CSLUL_P_IMPL); struct IdentDecl *f; struct FuncBody *fb; struct Stmt *s, *se; TEST_SOURCE("func f() -> int\n" "{\n" " if 0 == 1 return -123\n" " else if 2 == 3 {\n" " if 4 == 5 { assert 1==1 }\n" " } else if 6 == 7 return 123\n" " else {\n" " if 8 == 9 return 12+(345-67)\n" " else return 89-123\n" " }\n" " return 456\n" "}\n", 1); f = lookup_toplevel(ctx, "f"); assert_intfunc(f); tassert((fb = f->u.funcbody) != NULL); tsoftassert(fb->stmtblock.base == NULL); tsoftassert(fb->stmtblock.idents == NULL); s = &fb->stmtblock.stmt; /* [line 3] if 0 == 1 ... */ ASSERT_STMT_IF_WITH_ELSE(s); ASSERT_EXPR_INT_EQ(s->u.ifstm->cond, 0, 1); /* [line 3] ... return -123 */ ASSERT_STMT_RETURN(&s->u.ifstm->true_block.stmt); tsoftassert(s->u.ifstm->true_block.stmt.next == NULL); se = &s->u.ifstm->false_block->stmt; /* [line 4] else if 2 == 3 { */ ASSERT_STMT_IF_WITH_ELSE(se); ASSERT_EXPR_INT_EQ(se->u.ifstm->cond, 2, 3); { /* [line 5] if 4 == 5 { ... */ struct Stmt *s1 = &se->u.ifstm->true_block.stmt; ASSERT_STMT_IF(s1); ASSERT_EXPR_INT_EQ(s1->u.ifstm->cond, 4, 5); /* [line 5] ... assert 1==1 } */ ASSERT_STMT_ASSERT_EQ(&s1->u.ifstm->true_block.stmt, 1, 1); tsoftassert(s1->next == NULL); } tsoftassert(se->next == NULL); /* "next" makes no sense for else block */ se = &se->u.ifstm->false_block->stmt; /* [line 6] } else if 6 == 7 ... */ ASSERT_STMT_IF_WITH_ELSE(se); ASSERT_EXPR_INT_EQ(se->u.ifstm->cond, 6, 7); /* [line 6] ... return 123 */ ASSERT_STMT_RETURN(&se->u.ifstm->true_block.stmt); tsoftassert(se->u.ifstm->true_block.stmt.next == NULL); /* [line 7] else { */ { struct Stmt *s2 = &se->u.ifstm->false_block->stmt; /* [line 8] if 8 == 9 ... */ ASSERT_STMT_IF_WITH_ELSE(s2); ASSERT_EXPR_INT_EQ(s2->u.ifstm->cond, 8, 9); /* [line 8] ... return 12+(345-67) */ ASSERT_STMT_RETURN(&s2->u.ifstm->true_block.stmt); tsoftassert(s2->u.ifstm->true_block.stmt.next == NULL); /* [line 9] else return 89-123 */ ASSERT_STMT_RETURN(&s2->u.ifstm->false_block->stmt); tsoftassert(s2->u.ifstm->false_block->stmt.next == NULL); /* [line 10] } */ } s = s->next; /* [line 11] return 456 */ ASSERT_STMT_RETURN(s); tsoftassert(s->next == NULL); tsoftassert(fb->next == NULL); free_assert(ctx); } static void test_parse_funcbody_else_noif1(void) { struct CSlul *ctx = create_ctx(CSLUL_P_IMPL); expect_error(CSLUL_E_NOIFEND, 3, 5); TEST_SOURCE("func f() -> int\n" "{\n" " else if 2 == 3 {\n" " if 4 == 5 { assert 1==1 }\n" " } else if 6 == 7 return 123\n" " else {\n" " if 8 == 9 return 12+(345-67)\n" " else return 89-123\n" " }\n" " return 456\n" "}\n", 1); free_assert(ctx); } static void test_parse_funcbody_else_noif2(void) { struct CSlul *ctx = create_ctx(CSLUL_P_IMPL); expect_error(CSLUL_E_NOIFEND, 7, 5); TEST_SOURCE("func f() -> int\n" "{\n" " if 1 == 2 {\n" " if 4 == 5 { assert 1==1 }\n" " }\n" " assert 2==2\n" " else { assert 3==3 }\n" " return 123\n" "}\n", 1); free_assert(ctx); } static void test_parse_funcbody_else_noif3(void) { struct CSlul *ctx = create_ctx(CSLUL_P_IMPL); expect_error(CSLUL_E_NOIFEND, 4, 5); TEST_SOURCE("func f() -> int\n" "{\n" " while 1 == 2 return 123\n" " else return 123\n" "}\n", 1); free_assert(ctx); } static void test_parse_funcbody_while(void) { struct CSlul *ctx = create_ctx(CSLUL_P_IMPL); struct IdentDecl *f; struct FuncBody *fb; struct Stmt *s; TEST_SOURCE("func f()\n" "{\n" " while 0 == 1 return\n" " while 2 == 3 {\n" " while 4 == 5 { assert 1 == 1 }\n" " while 6 == 7 return\n" " while 8 == 9 { }\n" " }\n" " while 10 == 11 return\n" "}\n", 1); f = lookup_toplevel(ctx, "f"); assert_voidfunc(f); tassert((fb = f->u.funcbody) != NULL); tsoftassert(fb->stmtblock.base == NULL); tsoftassert(fb->stmtblock.idents == NULL); s = &fb->stmtblock.stmt; ASSERT_STMT_WHILE(s); ASSERT_EXPR_INT_EQ(s->u.whilestm->cond, 0, 1); ASSERT_STMT_RETURN(&s->u.whilestm->l.block.stmt); tsoftassert(s->u.whilestm->l.block.stmt.next == NULL); s = s->next; ASSERT_STMT_WHILE(s); ASSERT_EXPR_INT_EQ(s->u.whilestm->cond, 2, 3); { struct Stmt *s1 = &s->u.whilestm->l.block.stmt; ASSERT_STMT_WHILE(s1); ASSERT_EXPR_INT_EQ(s1->u.whilestm->cond, 4, 5); s1 = s1->next; ASSERT_STMT_WHILE(s1); ASSERT_EXPR_INT_EQ(s1->u.whilestm->cond, 6, 7); ASSERT_STMT_RETURN(&s1->u.whilestm->l.block.stmt); s1 = s1->next; ASSERT_STMT_WHILE(s1); ASSERT_EXPR_INT_EQ(s1->u.whilestm->cond, 8, 9); ASSERT_STMT_NOP(&s1->u.whilestm->l.block.stmt); tsoftassert(s1->next == NULL); } s = s->next; ASSERT_STMT_WHILE(s); ASSERT_EXPR_INT_EQ(s->u.whilestm->cond, 10, 11); ASSERT_STMT_RETURN(&s->u.whilestm->l.block.stmt); tsoftassert(fb->next == NULL); free_assert(ctx); } static void test_parse_funcbody_dowhile(void) { struct CSlul *ctx = create_ctx(CSLUL_P_IMPL); struct IdentDecl *f; struct FuncBody *fb; struct Stmt *s; TEST_SOURCE("func f()\n" "{\n" " do { } while 0 == 1\n" " do {\n" " do { assert 1 == 1 } while 4 == 5\n" " do { return } while 6 == 7\n" " do { } while 8 == 9\n" " } while 2 == 3\n" " do { return } while 10 == 11\n" " while 12 == 13 { }\n" "}\n", 1); f = lookup_toplevel(ctx, "f"); assert_voidfunc(f); tassert((fb = f->u.funcbody) != NULL); tsoftassert(fb->stmtblock.base == NULL); tsoftassert(fb->stmtblock.idents == NULL); s = &fb->stmtblock.stmt; ASSERT_STMT_DOWHILE(s); ASSERT_EXPR_INT_EQ(s->u.whilestm->cond, 0, 1); ASSERT_STMT_NOP(&s->u.whilestm->l.block.stmt); tsoftassert(s->u.whilestm->l.block.stmt.next == NULL); s = s->next; ASSERT_STMT_DOWHILE(s); ASSERT_EXPR_INT_EQ(s->u.whilestm->cond, 2, 3); { struct Stmt *s1 = &s->u.whilestm->l.block.stmt; ASSERT_STMT_DOWHILE(s1); ASSERT_EXPR_INT_EQ(s1->u.whilestm->cond, 4, 5); s1 = s1->next; ASSERT_STMT_DOWHILE(s1); ASSERT_EXPR_INT_EQ(s1->u.whilestm->cond, 6, 7); ASSERT_STMT_RETURN(&s1->u.whilestm->l.block.stmt); s1 = s1->next; ASSERT_STMT_DOWHILE(s1); ASSERT_EXPR_INT_EQ(s1->u.whilestm->cond, 8, 9); ASSERT_STMT_NOP(&s1->u.whilestm->l.block.stmt); tsoftassert(s1->next == NULL); } s = s->next; ASSERT_STMT_DOWHILE(s); ASSERT_EXPR_INT_EQ(s->u.whilestm->cond, 10, 11); ASSERT_STMT_RETURN(&s->u.whilestm->l.block.stmt); tsoftassert(s->u.whilestm->l.block.stmt.next == NULL); s = s->next; ASSERT_STMT_WHILE(s); ASSERT_EXPR_INT_EQ(s->u.whilestm->cond, 12, 13); ASSERT_STMT_NOP(&s->u.whilestm->l.block.stmt); tsoftassert(s->u.whilestm->l.block.stmt.next == NULL); tsoftassert(fb->next == NULL); free_assert(ctx); } static void test_parse_funcbody_dowhile_nowhile1(void) { struct CSlul *ctx = create_ctx(CSLUL_P_IMPL); expect_error(CSLUL_E_DOWITHOUTWHILE, 3, 12); TEST_SOURCE("func f() -> int\n" "{\n" " do { } return 1\n" "}\n", 1); free_assert(ctx); } static void test_parse_funcbody_dowhile_nowhile2(void) { struct CSlul *ctx = create_ctx(CSLUL_P_IMPL); expect_error(CSLUL_E_DOWITHOUTWHILE, 4, 1); TEST_SOURCE("func f() -> int\n" "{\n" " do { }\n" "}\n", 1); free_assert(ctx); } static void test_parse_funcbody_dowhile_nobraces(void) { struct CSlul *ctx = create_ctx(CSLUL_P_IMPL); expect_error(CSLUL_E_LOOPWITHOUTBRACES, 3, 8); TEST_SOURCE("func f() -> int\n" "{\n" " do continue while 1 == 2\n" "}\n", 1); free_assert(ctx); } static void test_parse_funcbody_for_notype1(void) { struct CSlul *ctx = create_ctx(CSLUL_P_IMPL); expect_error(CSLUL_E_BADTYPE, 3, 9); TEST_SOURCE("func f()\n" "{\n" " for {", 0); free_assert(ctx); } static void test_parse_funcbody_for_noident1(void) { struct CSlul *ctx = create_ctx(CSLUL_P_IMPL); expect_error(CSLUL_E_IDENTIFIEREXPECTED, 3, 14); TEST_SOURCE("func f()\n" "{\n" " for byte {", 0); free_assert(ctx); } static void test_parse_funcbody_for_noin1(void) { struct CSlul *ctx = create_ctx(CSLUL_P_IMPL); expect_error(CSLUL_E_NOIN, 3, 15); TEST_SOURCE("func f()\n" "{\n" " for byte b {", 0); free_assert(ctx); } static void test_parse_funcbody_for_noexpr1(void) { struct CSlul *ctx = create_ctx(CSLUL_P_IMPL); expect_error(CSLUL_E_INCOMPLETEEXPR, 3, 19); TEST_SOURCE("func f()\n" "{\n" " for byte b in {", 0); free_assert(ctx); } static void test_parse_funcbody_for_badqual(void) { struct CSlul *ctx = create_ctx(CSLUL_P_IMPL); expect_error(CSLUL_E_QUALNOTALLOWED, 3, 9); TEST_SOURCE("func f()\n" "{\n" " for writeonly byte b in", 0); free_assert(ctx); } static void test_parse_funcbody_breakcontinue(void) { struct CSlul *ctx = create_ctx(CSLUL_P_IMPL); struct IdentDecl *f; struct FuncBody *fb; struct Stmt *s; TEST_SOURCE("func f()\n" "{\n" " do { break } while 0 == 1\n" " do {\n" " while 4 == 5 {\n" " if 6 == 7 break\n" " if 8 == 9 continue\n" " else break\n" " }\n" " if 10 == 11 break\n" " continue\n" " } while 2 == 3\n" "}\n", 1); f = lookup_toplevel(ctx, "f"); assert_voidfunc(f); tassert((fb = f->u.funcbody) != NULL); tsoftassert(fb->stmtblock.base == NULL); tsoftassert(fb->stmtblock.idents == NULL); s = &fb->stmtblock.stmt; /* [line 3] do { break } while 0 == 1 */ ASSERT_STMT_DOWHILE(s); ASSERT_EXPR_INT_EQ(s->u.whilestm->cond, 0, 1); ASSERT_STMT_BREAK(&s->u.whilestm->l.block.stmt, &s->u.whilestm->l); tsoftassert(s->u.whilestm->l.block.stmt.next == NULL); /* [line 4] do { */ s = s->next; ASSERT_STMT_DOWHILE(s); ASSERT_EXPR_INT_EQ(s->u.whilestm->cond, 2, 3); { struct Stmt *s1 = &s->u.whilestm->l.block.stmt; /* [line 5] while 4 == 5 { */ ASSERT_STMT_WHILE(s1); ASSERT_EXPR_INT_EQ(s1->u.whilestm->cond, 4, 5); { /* [line 6] if 6 == 7 break */ struct Stmt *s2 = &s1->u.whilestm->l.block.stmt; ASSERT_STMT_IF(s2); ASSERT_EXPR_INT_EQ(s2->u.ifstm->cond, 6, 7); ASSERT_STMT_BREAK(&s2->u.ifstm->true_block.stmt, &s1->u.whilestm->l); /* [line 7] if 8 == 9 continue */ s2 = s2->next; ASSERT_STMT_IF_WITH_ELSE(s2); ASSERT_EXPR_INT_EQ(s2->u.ifstm->cond, 8, 9); ASSERT_STMT_CONTINUE(&s2->u.ifstm->true_block.stmt, &s1->u.whilestm->l); /* [line 8] else break */ tsoftassert(s2->u.ifstm->false_block != NULL); ASSERT_STMT_BREAK(&s2->u.ifstm->false_block->stmt, &s1->u.whilestm->l); tsoftassert(s2->next == NULL); } /* [line 10] if 10 == 11 break */ s1 = s1->next; ASSERT_STMT_IF(s1); ASSERT_EXPR_INT_EQ(s1->u.ifstm->cond, 10, 11); ASSERT_STMT_BREAK(&s1->u.ifstm->true_block.stmt, &s->u.whilestm->l); /* [line 11] continue */ s1 = s1->next; ASSERT_STMT_CONTINUE(s1, &s->u.whilestm->l); tsoftassert(s1->next == NULL); } tsoftassert(s->next == NULL); tsoftassert(fb->next == NULL); free_assert(ctx); } static void test_parse_funcbody_breakcontinue_noloop1(void) { struct CSlul *ctx = create_ctx(CSLUL_P_IMPL); expect_error(CSLUL_E_BREAKOUTSIDELOOP, 3, 5); TEST_SOURCE("func f() -> int\n" "{\n" " break\n" "}\n", 1); free_assert(ctx); } static void test_parse_funcbody_breakcontinue_noloop2(void) { struct CSlul *ctx = create_ctx(CSLUL_P_IMPL); expect_error(CSLUL_E_CONTINUEOUTSIDELOOP, 3, 15); TEST_SOURCE("func f() -> int\n" "{\n" " if 0 == 1 continue\n" "}\n", 1); free_assert(ctx); } static void test_parse_funcbody_breakcontinue_noloop3(void) { struct CSlul *ctx = create_ctx(CSLUL_P_IMPL); expect_error(CSLUL_E_BREAKOUTSIDELOOP, 4, 5); TEST_SOURCE("func f() -> int\n" "{\n" " while 0 == 1 { }\n" " break\n" "}\n", 1); free_assert(ctx); } static void test_parse_funcbody_loopempty(void) { struct CSlul *ctx = create_ctx(CSLUL_P_IMPL); struct IdentDecl *f; struct FuncBody *fb; struct Stmt *s; TEST_SOURCE("func f()\n" "{\n" " while 0 == 1 {\n" " while 2 == 3 {\n" " if 4 == 5 break\n" " } loopempty {\n" " assert 2 == 2\n" " }\n" " } loopempty return\n" "}\n", 1); f = lookup_toplevel(ctx, "f"); assert_voidfunc(f); tassert((fb = f->u.funcbody) != NULL); tsoftassert(fb->stmtblock.base == NULL); tsoftassert(fb->stmtblock.idents == NULL); s = &fb->stmtblock.stmt; /* [line 3] while 0 == 1 { */ ASSERT_STMT_WHILE_LOOPEMPTY(s); ASSERT_EXPR_INT_EQ(s->u.whilestm->cond, 0, 1); { struct Stmt *s1 = &s->u.whilestm->l.block.stmt; /* [line 4] while 2 == 3 { */ ASSERT_STMT_WHILE_LOOPEMPTY(s1); ASSERT_EXPR_INT_EQ(s1->u.whilestm->cond, 2, 3); /* [line 5] if 4 == 5 ... */ ASSERT_STMT_IF(&s1->u.whilestm->l.block.stmt); tsoftassert(s1->u.whilestm->l.block.stmt.next == NULL); tsoftassert(s1->next == NULL); { /* [line 5] ... break */ struct Stmt *s2 = &s1->u.whilestm->l.block.stmt.u.ifstm->true_block.stmt; ASSERT_STMT_BREAK(s2, &s1->u.whilestm->l); } /* [line 6] } loopend { */ { /* [line 7] assert 2 == 2 */ struct Stmt *s2 = &s1->u.whilestm->l.empty_block->stmt; ASSERT_STMT_ASSERT_EQ(s2, 2, 2); tsoftassert(s->u.whilestm->l.empty_block->stmt.next == NULL); } } { /* [line 8] } loopend ... */ struct Stmt *s1 = &s->u.whilestm->l.empty_block->stmt; /* [line 8] ... return */ ASSERT_STMT_RETURN(s1); tsoftassert(s1->next == NULL); } tsoftassert(s->next == NULL); tsoftassert(fb->next == NULL); free_assert(ctx); } static void test_parse_funcbody_loopend(void) { struct CSlul *ctx = create_ctx(CSLUL_P_IMPL); struct IdentDecl *f; struct FuncBody *fb; struct Stmt *s; TEST_SOURCE("func f()\n" "{\n" " while 0 == 1 {\n" " while 2 == 3 {\n" " if 4 == 5 break\n" " } loopend break\n" " } loopend {\n" " assert 2 == 2\n" " }\n" "}\n", 1); f = lookup_toplevel(ctx, "f"); assert_voidfunc(f); tassert((fb = f->u.funcbody) != NULL); tsoftassert(fb->stmtblock.base == NULL); tsoftassert(fb->stmtblock.idents == NULL); s = &fb->stmtblock.stmt; /* [line 3] while 0 == 1 { */ ASSERT_STMT_WHILE_LOOPEND(s); ASSERT_EXPR_INT_EQ(s->u.whilestm->cond, 0, 1); { struct Stmt *s1 = &s->u.whilestm->l.block.stmt; /* [line 4] while 2 == 3 { */ ASSERT_STMT_WHILE_LOOPEND(s1); ASSERT_EXPR_INT_EQ(s1->u.whilestm->cond, 2, 3); /* [line 5] if 4 == 5 ... */ ASSERT_STMT_IF(&s1->u.whilestm->l.block.stmt); tsoftassert(s1->u.whilestm->l.block.stmt.next == NULL); tsoftassert(s1->next == NULL); { /* [line 5] ... break */ struct Stmt *s2 = &s1->u.whilestm->l.block.stmt.u.ifstm->true_block.stmt; ASSERT_STMT_BREAK(s2, &s1->u.whilestm->l); } /* [line 6] } loopend break */ ASSERT_STMT_BREAK(&s1->u.whilestm->l.end_block->stmt, &s->u.whilestm->l); tsoftassert(s->u.whilestm->l.end_block->stmt.next == NULL); } { /* [line 7] } loopend { */ struct Stmt *s1 = &s->u.whilestm->l.end_block->stmt; /* [line 8] assert 2 == 2 */ ASSERT_STMT_ASSERT_EQ(s1, 2, 2); tsoftassert(s1->next == NULL); } tsoftassert(s->next == NULL); tsoftassert(fb->next == NULL); free_assert(ctx); } static void test_parse_funcbody_loopend_without_break(void) { struct CSlul *ctx = create_ctx(CSLUL_P_IMPL); expect_error(CSLUL_E_LOOPENDWITHOUTBREAK, 4, 5); TEST_SOURCE("func f()\n" "{\n" " while 0 == 1 { }\n" " loopend { }\n" "}\n", 1); free_assert(ctx); } static void test_parse_funcbody_loopemptyend_dowhile(void) { struct CSlul *ctx = create_ctx(CSLUL_P_IMPL); struct IdentDecl *f; struct FuncBody *fb; struct Stmt *s; TEST_SOURCE("func f()\n" "{\n" " do {\n" " do {\n" " if 4 == 5 break\n" " } while 2 == 3\n" " loopend break\n" " } while 0 == 1\n" " loopempty {\n" " assert 2 == 2\n" " } loopend {\n" " assert 3 == 3\n" " }\n" "}\n", 1); f = lookup_toplevel(ctx, "f"); assert_voidfunc(f); tassert((fb = f->u.funcbody) != NULL); tsoftassert(fb->stmtblock.base == NULL); tsoftassert(fb->stmtblock.idents == NULL); s = &fb->stmtblock.stmt; /* [line 3] do { --> } while 0 == 1 */ ASSERT_STMT_DOWHILE_LOOPEMPTYEND(s); ASSERT_EXPR_INT_EQ(s->u.whilestm->cond, 0, 1); { struct Stmt *s1 = &s->u.whilestm->l.block.stmt; /* [line 4] do { --> } while 2 == 3 { */ ASSERT_STMT_DOWHILE_LOOPEND(s1); ASSERT_EXPR_INT_EQ(s1->u.whilestm->cond, 2, 3); /* [line 5] if 4 == 5 ... */ ASSERT_STMT_IF(&s1->u.whilestm->l.block.stmt); tsoftassert(s1->u.whilestm->l.block.stmt.next == NULL); tsoftassert(s1->next == NULL); { /* [line 5] ... break */ struct Stmt *s2 = &s1->u.whilestm->l.block.stmt.u.ifstm->true_block.stmt; ASSERT_STMT_BREAK(s2, &s1->u.whilestm->l); } /* [line 7] loopend break */ ASSERT_STMT_BREAK(&s1->u.whilestm->l.end_block->stmt, &s->u.whilestm->l); tsoftassert(s->u.whilestm->l.end_block->stmt.next == NULL); } { /* [line 9] loopempty { */ struct Stmt *s1 = &s->u.whilestm->l.empty_block->stmt; /* [line 10] assert 2 == 2 */ ASSERT_STMT_ASSERT_EQ(s1, 2, 2); tsoftassert(s1->next == NULL); } { /* [line 11] } loopend { */ struct Stmt *s1 = &s->u.whilestm->l.end_block->stmt; /* [line 12] assert 3 == 3 */ ASSERT_STMT_ASSERT_EQ(s1, 3, 3); tsoftassert(s1->next == NULL); } tsoftassert(s->next == NULL); tsoftassert(fb->next == NULL); free_assert(ctx); } static void test_parse_funcbody_loopemptyend_for(void) { struct CSlul *ctx = create_ctx(CSLUL_P_IMPL); struct IdentDecl *f; struct FuncBody *fb; struct Stmt *s; TEST_SOURCE("func f()\n" "{\n" " for int i in [1,2] {\n" " for int j in [3,4] {\n" " if 4 == 5 break\n" " for int k in [5,6] { }\n" " } loopend break\n" " } loopempty {\n" " assert 2 == 2\n" " } loopend {\n" " assert 3 == 3\n" " }\n" "}\n", 1); f = lookup_toplevel(ctx, "f"); assert_voidfunc(f); ASSERT_LINECOL(&f->ident, 1, 6); tassert((fb = f->u.funcbody) != NULL); tsoftassert(fb->stmtblock.base == NULL); tsoftassert(fb->stmtblock.idents == NULL); s = &fb->stmtblock.stmt; /* [line 3] for int i in [1,2] */ ASSERT_STMT_FOR_LOOPEMPTYEND(s, "i"); ASSERT_TYPE_INT(&s->u.forstm->loopvar.type); ASSERT_LINECOL(&s->u.forstm->loopvar.ident, 3, 13); { struct Stmt *s1 = &s->u.whilestm->l.block.stmt; /* [line 4] for int j in [3,4] */ ASSERT_STMT_FOR_LOOPEND(s1, "j"); ASSERT_TYPE_INT(&s1->u.forstm->loopvar.type); ASSERT_LINECOL(&s1->u.forstm->loopvar.ident, 4, 17); { struct Stmt *s2 = &s1->u.forstm->l.block.stmt; /* [line 5] if 4 == 5 ... */ ASSERT_STMT_IF(s2); { /* [line 5] ... break */ struct Stmt *s3 = &s2->u.ifstm->true_block.stmt; ASSERT_STMT_BREAK(s3, &s1->u.forstm->l); } /* [line 6] for int k in [5,6] { } */ s2 = s2->next; ASSERT_STMT_FOR(s2, "k"); ASSERT_STMT_NOP(&s2->u.forstm->l.block.stmt); tsoftassert(s2->next == NULL); } /* [line 7] loopend break */ ASSERT_STMT_BREAK(&s1->u.forstm->l.end_block->stmt, &s->u.forstm->l); tsoftassert(s1->u.forstm->l.end_block->stmt.next == NULL); /* ??? */ } { /* [line 8] } loopempty { */ struct Stmt *s1 = &s->u.whilestm->l.empty_block->stmt; /* [line 9] assert 2 == 2 */ ASSERT_STMT_ASSERT_EQ(s1, 2, 2); tsoftassert(s1->next == NULL); } { /* [line 10] } loopend { */ struct Stmt *s1 = &s->u.whilestm->l.end_block->stmt; /* [line 11] assert 3 == 3 */ ASSERT_STMT_ASSERT_EQ(s1, 3, 3); tsoftassert(s1->next == NULL); } tsoftassert(s->next == NULL); tsoftassert(fb->next == NULL); free_assert(ctx); } static void test_parse_funcbody_loopemptyend_singlestmt(void) { struct CSlul *ctx = create_ctx(CSLUL_P_IMPL); struct IdentDecl *f; struct FuncBody *fb; struct Stmt *s; TEST_SOURCE("func f() -> int\n" "{\n" " while 0 == 1 {\n" " do {\n" " if 4 == 5 break\n" " } while 2 == 3\n" " loopempty break\n" " } loopempty return 123\n" " loopend return 456\n" " return 789\n" "}\n", 1); f = lookup_toplevel(ctx, "f"); assert_intfunc(f); tassert((fb = f->u.funcbody) != NULL); tsoftassert(fb->stmtblock.base == NULL); tsoftassert(fb->stmtblock.idents == NULL); s = &fb->stmtblock.stmt; /* [line 3] while 0 == 1 */ ASSERT_STMT_WHILE_LOOPEMPTYEND(s); ASSERT_EXPR_INT_EQ(s->u.whilestm->cond, 0, 1); { struct Stmt *s1 = &s->u.whilestm->l.block.stmt; /* [line 4] do { --> } while 2 == 3 { */ ASSERT_STMT_DOWHILE_LOOPEMPTY(s1); ASSERT_EXPR_INT_EQ(s1->u.whilestm->cond, 2, 3); /* [line 5] if 4 == 5 ... */ ASSERT_STMT_IF(&s1->u.whilestm->l.block.stmt); tsoftassert(s1->u.whilestm->l.block.stmt.next == NULL); tsoftassert(s1->next == NULL); { /* [line 5] ... break */ struct Stmt *s2 = &s1->u.whilestm->l.block.stmt.u.ifstm->true_block.stmt; ASSERT_STMT_BREAK(s2, &s1->u.whilestm->l); } /* [line 7] loopempty break */ ASSERT_STMT_BREAK(&s1->u.whilestm->l.empty_block->stmt, &s->u.whilestm->l); tsoftassert(s->u.whilestm->l.end_block->stmt.next == NULL); } /* [line 8] } loopempty return 123 */ ASSERT_STMT_RETURN_INT(&s->u.whilestm->l.empty_block->stmt, 123); tsoftassert(s->u.whilestm->l.empty_block->stmt.next == NULL); /* [line 9] loopend return 456 */ ASSERT_STMT_RETURN_INT(&s->u.whilestm->l.end_block->stmt, 456); tsoftassert(s->u.whilestm->l.end_block->stmt.next == NULL); /* [line 10] return 789 */ s = s->next; ASSERT_STMT_RETURN_INT(s, 789); tsoftassert(s->next == NULL); tsoftassert(fb->next == NULL); free_assert(ctx); } static void test_parse_funcbody_loopemptyend_scope(void) { struct CSlul *ctx = create_ctx(CSLUL_P_IMPL); struct IdentDecl *f; struct TreeNode *i_outer, *i_loopvar; struct FuncBody *fb; struct Stmt *s; TEST_SOURCE("func f()\n" "{\n" " int i = 123\n" " for int i in [1,2,3] {\n" " if 2 == 3 break\n" " } loopempty {\n" " int x = i\n" /* This should refer to the outer i */ " }\n" "}\n", 1); f = lookup_toplevel(ctx, "f"); assert_voidfunc(f); ASSERT_LINECOL(&f->ident, 1, 6); tassert((fb = f->u.funcbody) != NULL); tsoftassert(fb->stmtblock.base == NULL); tsoftassert(fb->stmtblock.idents != NULL); s = &fb->stmtblock.stmt; /* [line 3] int i */ ASSERT_STMT_DECL(s, "i"); i_outer = &s->u.vardef->decl.ident; ASSERT_LINECOL(i_outer, 3, 9); s = s->next; /* [line 4] for int i in [1,2,3] */ ASSERT_STMT_FOR_LOOPEMPTY(s, "i"); ASSERT_TYPE_INT(&s->u.forstm->loopvar.type); i_loopvar = &s->u.forstm->loopvar.ident; ASSERT_LINECOL(i_loopvar, 4, 13); /* [line 5] if 2 == 3 break -- not checked */ /* [line 6] } loopempty { */ { struct StmtBlock *block = s->u.forstm->l.empty_block; struct Stmt *s1 = &block->stmt; struct ExprRoot *initval; /* [line 7] int x = i */ ASSERT_STMT_DECL(s1, "x"); ASSERT_LINECOL(&s1->u.vardef->decl.ident, 7, 13); tsoftassert(block->idents == &s1->u.vardef->decl.ident); initval = s1->u.vardef->decl.u.initval; tassert(initval != NULL); tassert(initval->root != NULL); ASSERT_EXPR_IDENT(initval->root, "i"); tsoftassert(initval->root->a.ident != i_loopvar); tsoftassert(initval->root->a.ident == i_outer); } free_assert(ctx); } static void test_parse_funcbody_loopemptyend_wrongorder(void) { struct CSlul *ctx = create_ctx(CSLUL_P_IMPL); expect_error(CSLUL_E_LOOPEMPTYENDORDER, 6, 5); TEST_SOURCE("func f()\n" "{\n" " while 0 == 1 {\n" " if 2 == 3 break\n" " } loopend { }\n" " loopempty { }\n" "}\n", 1); free_assert(ctx); } static void test_parse_funcbody_loopemptyend_badbreak(void) { struct CSlul *ctx = create_ctx(CSLUL_P_IMPL); expect_error(CSLUL_E_BREAKOUTSIDELOOP, 4, 15); TEST_SOURCE("func f()\n" "{\n" " while 0 == 1 { }\n" " loopempty break\n" "}\n", 1); free_assert(ctx); } static void test_parse_funcbody_loopempty_duplicate(void) { struct CSlul *ctx = create_ctx(CSLUL_P_IMPL); expect_error(CSLUL_E_DUPLICATELOOPEMPTY, 6, 5); TEST_SOURCE("func f()\n" "{\n" " while 0 == 1 {\n" " if 2 == 3 break\n" " } loopempty { }\n" " loopempty { }\n" "}\n", 1); free_assert(ctx); } static void test_parse_funcbody_loopend_duplicate(void) { struct CSlul *ctx = create_ctx(CSLUL_P_IMPL); expect_error(CSLUL_E_DUPLICATELOOPEND, 6, 5); TEST_SOURCE("func f()\n" "{\n" " while 0 == 1 {\n" " if 2 == 3 break\n" " } loopend { }\n" " loopend { }\n" "}\n", 1); free_assert(ctx); } static void test_parse_funcbody_goto_forward(void) { struct CSlul *ctx = create_ctx(CSLUL_P_IMPL); struct IdentDecl *f; struct FuncBody *fb; struct Stmt *s; TEST_SOURCE("func f()\n" "{\n" " goto end\n" " end:\n" "}\n", 1); f = lookup_toplevel(ctx, "f"); assert_voidfunc(f); tassert((fb = f->u.funcbody) != NULL); tsoftassert(fb->stmtblock.base == NULL); tsoftassert(fb->stmtblock.idents == NULL); s = &fb->stmtblock.stmt; ASSERT_STMT_GOTO(s, "end"); ASSERT_STMT_TARGET(s->next, "end"); tsoftassert(s->u.gotoident == s->next->u.gotoident); tsoftassert(s->u.gotoident->node.line == 4); tsoftassert(s->u.gotoident->node.column == 3); tsoftassert(s->u.gotoident->seen_line == 3); tsoftassert(s->u.gotoident->seen_column == 10); tsoftassert(s->next->next == NULL); tsoftassert(fb->next == NULL); free_assert(ctx); } static void test_parse_funcbody_goto_backward(void) { struct CSlul *ctx = create_ctx(CSLUL_P_IMPL); struct IdentDecl *f; struct FuncBody *fb; struct Stmt *s; TEST_SOURCE("func f()\n" "{\n" " start:\n" " goto start\n" "}\n", 1); f = lookup_toplevel(ctx, "f"); assert_voidfunc(f); tassert((fb = f->u.funcbody) != NULL); tsoftassert(fb->stmtblock.base == NULL); tsoftassert(fb->stmtblock.idents == NULL); s = &fb->stmtblock.stmt; ASSERT_STMT_TARGET(s, "start"); ASSERT_STMT_GOTO(s->next, "start"); tsoftassert(s->u.gotoident == s->next->u.gotoident); tsoftassert(s->u.gotoident->node.line == 3); tsoftassert(s->u.gotoident->node.column == 3); tsoftassert(s->u.gotoident->seen_line == 3); tsoftassert(s->u.gotoident->seen_column == 3); tsoftassert(s->next->next == NULL); tsoftassert(fb->next == NULL); free_assert(ctx); } static void test_parse_funcbody_goto_duplicate(void) { struct CSlul *ctx = create_ctx(CSLUL_P_IMPL); expect_error(CSLUL_E_DUPLICATEGOTO, 5, 3); TEST_SOURCE("func f()\n" "{\n" " target1:\n" " goto target1\n" " target1:\n" "}\n", 1); free_assert(ctx); } static void test_parse_funcbody_goto_missing(void) { struct CSlul *ctx = create_ctx(CSLUL_P_IMPL); expect_error(CSLUL_E_UNDEFINEDGOTO, 3, 10); TEST_SOURCE("func f()\n" "{\n" " goto end\n" "}\n", 1); free_assert(ctx); } static void test_parse_funcbody_switch_default(void) { struct CSlul *ctx = create_ctx(CSLUL_P_IMPL); struct IdentDecl *f; struct FuncBody *fb; struct Stmt *s; TEST_SOURCE("func f()\n" "{\n" " int i = 0\n" " switch i {\n" " default:\n" " assert 1==1\n" " }\n" "}\n", 1); f = lookup_toplevel(ctx, "f"); assert_voidfunc(f); tassert((fb = f->u.funcbody) != NULL); tsoftassert(fb->stmtblock.base == NULL); tsoftassert(fb->stmtblock.idents != NULL); s = &fb->stmtblock.stmt; ASSERT_STMT_DECL(s, "i"); s = s->next; ASSERT_STMT_SWITCH(s); tassert(s->u.switchstm->cond->root != NULL); ASSERT_EXPR_IDENT(s->u.switchstm->cond->root, "i"); tsoftassert(s->u.switchstm->has_default); tassert(s->u.switchstm->cases != NULL); tassert(s->u.switchstm->cases->values.expr == NULL); tassert(s->u.switchstm->cases->values.next == NULL); tassert(s->u.switchstm->cases->next == NULL); tassert(s->u.switchstm->cases->block.idents == NULL); { struct Stmt *s1 = &s->u.switchstm->cases->block.stmt; ASSERT_STMT_ASSERT_EQ(s1, 1, 1); tsoftassert(s1->next == NULL); } tsoftassert(s->next == NULL); tsoftassert(fb->next == NULL); free_assert(ctx); } static void test_parse_funcbody_switch_case(void) { struct CSlul *ctx = create_ctx(CSLUL_P_IMPL); struct IdentDecl *f; struct FuncBody *fb; struct Stmt *s; TEST_SOURCE("func f()\n" "{\n" " int i = 123\n" " switch i {\n" " case 123:\n" " assert 1==1\n" " }\n" "}\n", 1); f = lookup_toplevel(ctx, "f"); assert_voidfunc(f); tassert((fb = f->u.funcbody) != NULL); tsoftassert(fb->stmtblock.base == NULL); tsoftassert(fb->stmtblock.idents != NULL); s = &fb->stmtblock.stmt; ASSERT_STMT_DECL(s, "i"); s = s->next; ASSERT_STMT_SWITCH(s); tassert(s->u.switchstm->cond->root != NULL); ASSERT_EXPR_IDENT(s->u.switchstm->cond->root, "i"); tsoftassert(!s->u.switchstm->has_default); tassert(s->u.switchstm->cases != NULL); ASSERT_EXPR_INT(s->u.switchstm->cases->values.expr, 123); tsoftassert(s->u.switchstm->cases->values.next == NULL); tsoftassert(s->u.switchstm->cases->next == NULL); tsoftassert(s->u.switchstm->cases->block.idents == NULL); { struct Stmt *s1 = &s->u.switchstm->cases->block.stmt; ASSERT_STMT_ASSERT_EQ(s1, 1, 1); tsoftassert(s1->next == NULL); } tsoftassert(s->next == NULL); tsoftassert(fb->next == NULL); free_assert(ctx); } static void test_parse_funcbody_switch_case2val(void) { struct CSlul *ctx = create_ctx(CSLUL_P_IMPL); struct IdentDecl *f; struct FuncBody *fb; struct CaseValue *caseval; struct Stmt *s; TEST_SOURCE("func f()\n" "{\n" " int i = 123\n" " switch i {\n" " case 123, 456:\n" " assert 1==1\n" " }\n" "}\n", 1); f = lookup_toplevel(ctx, "f"); assert_voidfunc(f); tassert((fb = f->u.funcbody) != NULL); tsoftassert(fb->stmtblock.base == NULL); tsoftassert(fb->stmtblock.idents != NULL); s = &fb->stmtblock.stmt; ASSERT_STMT_DECL(s, "i"); s = s->next; ASSERT_STMT_SWITCH(s); tassert(s->u.switchstm->cond->root != NULL); ASSERT_EXPR_IDENT(s->u.switchstm->cond->root, "i"); tsoftassert(!s->u.switchstm->has_default); tassert(s->u.switchstm->cases != NULL); caseval = &s->u.switchstm->cases->values; ASSERT_EXPR_INT(caseval->expr, 456); /* order is not preserved */ tassert((caseval = caseval->next) != NULL); ASSERT_EXPR_INT(caseval->expr, 123); tsoftassert(caseval->next == NULL); tsoftassert(s->u.switchstm->cases->next == NULL); tsoftassert(s->u.switchstm->cases->block.idents == NULL); { struct Stmt *s1 = &s->u.switchstm->cases->block.stmt; ASSERT_STMT_ASSERT_EQ(s1, 1, 1); tsoftassert(s1->next == NULL); } tsoftassert(s->next == NULL); tsoftassert(fb->next == NULL); free_assert(ctx); } static void test_parse_funcbody_switch_multiple(void) { struct CSlul *ctx = create_ctx(CSLUL_P_IMPL); struct IdentDecl *f; struct FuncBody *fb; struct CaseValue *caseval; struct SwitchCase *case1, *case2; struct Stmt *s; /* Combining case values with default is allowed, and can be used for self-documenting code and/or to cover e.g. enum values, while still supporting other values (e.g. from newer library versions, or for improved fault tolerance) */ TEST_SOURCE("func f()\n" "{\n" " int i = 123\n" " switch i {\n" " case 123, 456:\n" " assert 1==1\n" " case 768, default:\n" " assert 2==2\n" " }\n" "}\n", 1); f = lookup_toplevel(ctx, "f"); assert_voidfunc(f); tassert((fb = f->u.funcbody) != NULL); tsoftassert(fb->stmtblock.base == NULL); tsoftassert(fb->stmtblock.idents != NULL); s = &fb->stmtblock.stmt; /* [line 3] int i = 123 */ ASSERT_STMT_DECL(s, "i"); s = s->next; /* [line 4] switch i { */ ASSERT_STMT_SWITCH(s); tassert(s->u.switchstm->cond->root != NULL); ASSERT_EXPR_IDENT(s->u.switchstm->cond->root, "i"); tsoftassert(s->u.switchstm->has_default); /* [line 5] case 123, 456: */ tassert((case2 = s->u.switchstm->cases) != NULL); tassert((case1 = case2->next) != NULL); /* order isn't preserved */ tsoftassert(case1->next == NULL); caseval = &case1->values; ASSERT_EXPR_INT(caseval->expr, 456); /* order isn't preserved */ tassert((caseval = caseval->next) != NULL); ASSERT_EXPR_INT(caseval->expr, 123); tsoftassert(caseval->next == NULL); tsoftassert(case1->block.idents == NULL); { /* [line 6] assert 1==1 */ struct Stmt *s1 = &case1->block.stmt; ASSERT_STMT_ASSERT_EQ(s1, 1, 1); tsoftassert(s1->next == NULL); } /* [line 7] case 768, default: */ caseval = &case2->values; tsoftassert(caseval->expr == NULL); /* default */ tassert((caseval = caseval->next) != NULL); ASSERT_EXPR_INT(caseval->expr, 768); /* order isn't preserved */ tsoftassert(caseval->next == NULL); tsoftassert(case2->block.idents == NULL); { /* [line 8] assert 2==2 */ struct Stmt *s1 = &case2->block.stmt; ASSERT_STMT_ASSERT_EQ(s1, 2, 2); tsoftassert(s1->next == NULL); } tsoftassert(s->next == NULL); tsoftassert(fb->next == NULL); free_assert(ctx); } static void test_parse_funcbody_switch_subcase(void) { struct CSlul *ctx = create_ctx(CSLUL_P_IMPL); struct IdentDecl *f; struct FuncBody *fb; struct CaseValue *caseval; struct SwitchCase *case1, *case2; struct Stmt *s; TEST_SOURCE("func f()\n" "{\n" " int i = 111\n" " switch i {\n" " case 111, 222:\n" " subcase 222 {\n" " assert 2 == 2\n" " }\n" " switch i {\n" " case 333, 444:\n" " subcase 333 return\n" " default:\n" " assert 3 == 3\n" " }\n" " subcase 111 {\n" " assert 4 == 4\n" " }\n" " }\n" "}\n", 1); f = lookup_toplevel(ctx, "f"); assert_voidfunc(f); tassert((fb = f->u.funcbody) != NULL); tsoftassert(fb->stmtblock.base == NULL); tsoftassert(fb->stmtblock.idents != NULL); s = &fb->stmtblock.stmt; /* [line 3] int i = 111 */ ASSERT_STMT_DECL(s, "i"); s = s->next; /* [line 4] switch i { */ ASSERT_STMT_SWITCH(s); tassert(s->u.switchstm->cond->root != NULL); ASSERT_EXPR_IDENT(s->u.switchstm->cond->root, "i"); tsoftassert(!s->u.switchstm->has_default); /* [line 5] case 111, 222: */ tassert((case1 = s->u.switchstm->cases) != NULL); tsoftassert(case1->next == NULL); tsoftassert(case1->has_subcases); caseval = &case1->values; ASSERT_EXPR_INT(caseval->expr, 222); /* order isn't preserved */ tassert((caseval = caseval->next) != NULL); ASSERT_EXPR_INT(caseval->expr, 111); tsoftassert(caseval->next == NULL); tsoftassert(case1->block.idents == NULL); { /* [line 6] subcase 222 { */ struct Stmt *s1 = &case1->block.stmt; ASSERT_STMT_SUBCASE_INT(s1, 222); { struct Stmt *s2 = &s1->u.subcase->block.stmt; /* [line 7] assert 2 == 2 */ ASSERT_STMT_ASSERT_EQ(s2, 2, 2); tsoftassert(s2->next == NULL); } s1 = s1->next; /* [line 9] switch i { */ ASSERT_STMT_SWITCH(s1); tassert(s1->u.switchstm->cond->root != NULL); ASSERT_EXPR_IDENT(s1->u.switchstm->cond->root, "i"); tsoftassert(s1->u.switchstm->has_default); /* [line 10] case 333, 444: */ tassert((case2 = s1->u.switchstm->cases) != NULL); tassert((case1 = case2->next) != NULL); /* order isn't preserved */ tsoftassert(case1->next == NULL); tsoftassert(case1->has_subcases); caseval = &case1->values; ASSERT_EXPR_INT(caseval->expr, 444); /* order isn't preserved */ tassert((caseval = caseval->next) != NULL); ASSERT_EXPR_INT(caseval->expr, 333); tsoftassert(caseval->next == NULL); tsoftassert(case1->block.idents == NULL); { /* [line 11] subcase 333 ... */ struct Stmt *s2 = &case1->block.stmt; ASSERT_STMT_SUBCASE_INT(s2, 333); /* [line 11] ... return */ ASSERT_STMT_RETURN(&s2->u.subcase->block.stmt); tsoftassert(s2->next == NULL); } /* [line 12] default: */ caseval = &case2->values; tsoftassert(caseval->expr == NULL); tsoftassert(caseval->next == NULL); tsoftassert(!case2->has_subcases); tsoftassert(case2->block.idents == NULL); { /* [line 13] assert 3 == 3 */ struct Stmt *s2 = &case2->block.stmt; ASSERT_STMT_ASSERT_EQ(s2, 3, 3); tsoftassert(s2->next == NULL); } /* [line 15] subcase 111 { */ s1 = s1->next; ASSERT_STMT_SUBCASE_INT(s1, 111); /* [line 16] assert 4 == 4 */ tsoftassert(s1->next == NULL); } tsoftassert(s->next == NULL); tsoftassert(fb->next == NULL); free_assert(ctx); } static void test_parse_funcbody_switch_nolcurly(void) { struct CSlul *ctx = create_ctx(CSLUL_P_IMPL); expect_error(CSLUL_E_LCURLYEXPECTED, 4, 13); TEST_SOURCE("func f()\n" "{\n" " int i = 0\n" " switch i\n" "}\n", 1); free_assert(ctx); } static void test_parse_funcbody_switch_nocases(void) { struct CSlul *ctx = create_ctx(CSLUL_P_IMPL); expect_error(CSLUL_E_CASEEXPECTED, 5, 5); TEST_SOURCE("func f()\n" "{\n" " int i = 0\n" " switch i {\n" " }\n" "}\n", 1); free_assert(ctx); } static void test_parse_funcbody_switch_nodefaultcolon(void) { struct CSlul *ctx = create_ctx(CSLUL_P_IMPL); expect_error(CSLUL_E_NOCOLONAFTERDEFAULT, 5, 12); TEST_SOURCE("func f()\n" "{\n" " int i = 0\n" " switch i {\n" " default\n" " }\n" "}\n", 1); free_assert(ctx); } static void test_parse_funcbody_switch_nocasecolon(void) { struct CSlul *ctx = create_ctx(CSLUL_P_IMPL); expect_error(CSLUL_E_NOCASECOMMACOLON, 7, 13); expect_error(CSLUL_E_NOCASECOMMACOLON, 5, 13); TEST_SOURCE("func f()\n" "{\n" " var int i = 123\n" " switch i {\n" " case 123\n" " i = 456\n" " case 456\n" " }\n" "}\n", 1); free_assert(ctx); } static void test_parse_funcbody_switch_nocaseexpr(void) { struct CSlul *ctx = create_ctx(CSLUL_P_IMPL); expect_error(CSLUL_E_INCOMPLETEEXPR, 5, 10); TEST_SOURCE("func f()\n" "{\n" " int i = 123\n" " switch i {\n" " case :\n" " }\n" "}\n", 1); free_assert(ctx); } static void test_parse_funcbody_switch_badcaseexpr1(void) { struct CSlul *ctx = create_ctx(CSLUL_P_IMPL); expect_error(CSLUL_E_OPERATOREXPECTED, 5, 11); TEST_SOURCE("func f()\n" "{\n" " int i = 123\n" " switch i {\n" " case 1 2:\n" " }\n" "}\n", 1); free_assert(ctx); } static void test_parse_funcbody_switch_badcaseexpr2(void) { struct CSlul *ctx = create_ctx(CSLUL_P_IMPL); expect_error(CSLUL_E_INCOMPLETEEXPR, 5, 10); TEST_SOURCE("func f()\n" "{\n" " int i = 123\n" " switch i {\n" " case , 2:\n" " }\n" "}\n", 1); free_assert(ctx); } static void test_parse_funcbody_switch_repeatedcase(void) { struct CSlul *ctx = create_ctx(CSLUL_P_IMPL); expect_error(CSLUL_E_REPEATEDCASE, 6, 5); TEST_SOURCE("func f()\n" "{\n" " int i = 123\n" " switch i {\n" " case 123:\n" " case 456:\n" " }\n" "}\n", 1); free_assert(ctx); } static void test_parse_funcbody_switch_badsubcase1(void) { struct CSlul *ctx = create_ctx(CSLUL_P_IMPL); expect_error(CSLUL_E_SUBCASEOUTSIDECASE, 3, 5); TEST_SOURCE("func f()\n" "{\n" " subcase 1 {\n" " }\n" "}\n", 1); free_assert(ctx); } static void test_parse_funcbody_switch_badsubcase2(void) { struct CSlul *ctx = create_ctx(CSLUL_P_IMPL); expect_error(CSLUL_E_INCOMPLETEEXPR, 6, 17); TEST_SOURCE("func f()\n" "{\n" " int i = 123\n" " switch i {\n" " case 123:\n" " subcase return\n" " }\n" "}\n", 1); free_assert(ctx); } static void test_parse_funcbody_vardef_noinit(void) { struct CSlul *ctx = create_ctx(CSLUL_P_IMPL); struct IdentDecl *f, *var_i, *var_b; struct FuncBody *fb; struct Stmt *s; TEST_SOURCE("func f()\n" "{\n" " int i\n" " bool b\n" "}\n", 1); f = lookup_toplevel(ctx, "f"); assert_voidfunc(f); tassert((fb = f->u.funcbody) != NULL); tsoftassert(fb->stmtblock.base == NULL); tsoftassert(fb->stmtblock.idents != NULL); /* Check statements */ s = &fb->stmtblock.stmt; ASSERT_STMT_DECL(s, "i"); tassert(s->u.vardef != NULL); var_i = &s->u.vardef->decl; ASSERT_LINECOL(&var_i->ident, 3, 9); s = s->next; ASSERT_STMT_DECL(s, "b"); tassert(s->u.vardef != NULL); var_b = &s->u.vardef->decl; ASSERT_LINECOL(&var_b->ident, 4, 10); tsoftassert(s->next == NULL); /* Check identifiers */ tsoftassert(lookup_toplevel(ctx, "i") == NULL); tsoftassert(lookup_toplevel(ctx, "b") == NULL); tsoftassert(lookup(ctx, fb->stmtblock.idents, "i") == &var_i->ident); tsoftassert(lookup(ctx, fb->stmtblock.idents, "b") == &var_b->ident); tsoftassert(fb->next == NULL); free_assert(ctx); } static void test_parse_funcbody_vardef_init(void) { struct CSlul *ctx = create_ctx(CSLUL_P_IMPL); struct IdentDecl *f, *var_i, *var_b; struct FuncBody *fb; struct Stmt *s; TEST_SOURCE("func f()\n" "{\n" " int i = 123\n" " ref Thing b = .test\n" "}\n", 1); f = lookup_toplevel(ctx, "f"); assert_voidfunc(f); tassert((fb = f->u.funcbody) != NULL); tsoftassert(fb->stmtblock.base == NULL); tsoftassert(fb->stmtblock.idents != NULL); /* Check statements */ s = &fb->stmtblock.stmt; ASSERT_STMT_DECL(s, "i"); tassert(s->u.vardef != NULL); var_i = &s->u.vardef->decl; ASSERT_EXPR_INT(var_i->u.initval, 123); s = s->next; ASSERT_STMT_DECL(s, "b"); tassert(s->u.vardef != NULL); var_b = &s->u.vardef->decl; ASSERT_EXPR_TYPEIDENT(var_b->u.initval, "test"); tsoftassert(s->next == NULL); /* Check identifiers */ tsoftassert(lookup_toplevel(ctx, "i") == NULL); tsoftassert(lookup_toplevel(ctx, "b") == NULL); tsoftassert(lookup(ctx, fb->stmtblock.idents, "i") == &var_i->ident); tsoftassert(lookup(ctx, fb->stmtblock.idents, "b") == &var_b->ident); tsoftassert(fb->next == NULL); free_assert(ctx); } static void test_parse_funcbody_vardef_nested(void) { struct CSlul *ctx = create_ctx(CSLUL_P_IMPL); struct IdentDecl *f, *var_i, *nested; struct FuncBody *fb; struct Stmt *s; struct TreeNode *nested_idents_root; TEST_SOURCE("func f()\n" "{\n" " int i = 123\n" " while 1 == 2 {\n" " int i = 456\n" " }\n" "}\n", 1); f = lookup_toplevel(ctx, "f"); assert_voidfunc(f); tassert((fb = f->u.funcbody) != NULL); tsoftassert(fb->stmtblock.base == NULL); tsoftassert(fb->stmtblock.idents != NULL); /* Check statements */ s = &fb->stmtblock.stmt; ASSERT_STMT_DECL(s, "i"); tassert(s->u.vardef != NULL); var_i = &s->u.vardef->decl; ASSERT_EXPR_INT(var_i->u.initval, 123); s = s->next; ASSERT_STMT_WHILE(s); ASSERT_EXPR_INT_EQ(s->u.whilestm->cond, 1, 2); { struct Stmt *s1 = &s->u.whilestm->l.block.stmt; ASSERT_STMT_DECL(s1, "i"); tassert(s1->u.vardef != NULL); nested = &s1->u.vardef->decl; ASSERT_EXPR_INT(nested->u.initval, 456); } tsoftassert(s->next == NULL); /* Check identifiers */ tsoftassert(lookup_toplevel(ctx, "i") == NULL); tsoftassert(lookup(ctx, fb->stmtblock.idents, "i") == &var_i->ident); nested_idents_root = s->u.whilestm->l.block.idents; tsoftassert(lookup(ctx, nested_idents_root, "i") == &nested->ident); tsoftassert(var_i != nested); tsoftassert(fb->next == NULL); free_assert(ctx); } static void test_parse_funcbody_vardef_outofscope(void) { struct IdentDecl *f, *global_a; struct FuncBody *fb; struct CSlul *ctx = create_ctx(CSLUL_P_IMPL); TEST_SOURCE("func f() -> int\n" "{\n" " if 1 == 1 {\n" " int a = 123\n" " }\n" " int b = a+1\n" "}\n", 1); f = lookup_toplevel(ctx, "f"); assert_intfunc(f); tassert((fb = f->u.funcbody) != NULL); tsoftassert(lookup(ctx, fb->stmtblock.idents, "b") != NULL); /* "a" is defined in a nested block, and is then referenced as a global => it's not a local variable here */ tsoftassert(lookup(ctx, fb->stmtblock.idents, "a") == NULL); global_a = lookup_toplevel(ctx, "a"); tassert(global_a != NULL); tsoftassert(!IS_IDENT_LOCAL(global_a)); free_assert(ctx); } static void test_parse_funcbody_vardef_missingident(void) { struct CSlul *ctx = create_ctx(CSLUL_P_IMPL); expect_error(CSLUL_E_LOWERCASETYPE, 3, 9); TEST_SOURCE("func f()\n" "{\n" " var x = 1\n" "}\n", 1); free_assert(ctx); } static void test_parse_funcbody_nonewline_return(void) { struct CSlul *ctx = create_ctx(CSLUL_P_IMPL); expect_error(CSLUL_E_NORETURNNEWLINE, 3, 12); TEST_SOURCE("func f()\n" "{\n" " return 1\n" "}\n", 1); free_assert(ctx); } static void test_parse_funcbody_nonewline_rcurly(void) { struct CSlul *ctx = create_ctx(CSLUL_P_IMPL); expect_error(CSLUL_E_NONEWLINE, 3, 19); TEST_SOURCE("func f()\n" "{\n" " if 1 == 1 { } return\n" "}\n", 1); free_assert(ctx); } static void test_parse_funcbody_nonewline_expr1(void) { struct CSlul *ctx = create_ctx(CSLUL_P_IMPL); expect_error(CSLUL_E_NONEWLINE, 4, 13); TEST_SOURCE("func f()\n" "{\n" " var int i\n" " i = 123 return\n" "}\n", 1); free_assert(ctx); } static void test_parse_funcbody_nonewline_expr2(void) { struct CSlul *ctx = create_ctx(CSLUL_P_IMPL); expect_error(CSLUL_E_BADEXPRTOKEN, 4, 13); TEST_SOURCE("func f()\n" "{\n" " var int i\n" " i = 123 while\n" "}\n", 1); free_assert(ctx); } static void test_parse_funcbody_nonewline_expr3(void) { struct CSlul *ctx = create_ctx(CSLUL_P_IMPL); expect_error(CSLUL_E_BADEXPRTOKEN, 4, 13); TEST_SOURCE("func f()\n" "{\n" " var int i\n" " i = 123 else\n" "}\n", 1); free_assert(ctx); } static void test_parse_funcbody_mismatched_rcurly(void) { struct CSlul *ctx = create_ctx(CSLUL_P_IMPL); expect_error(CSLUL_E_TOOMANYRCURLY, 5, 5); TEST_SOURCE("func f()\n" "{\n" " if 1 == 0\n" /* missing { */ " return\n" " }\n" /* should recover and ignore this } */ " while 1 == 2 { }\n" "}\n", 1); free_assert(ctx); } static void test_parse_funcbody_skip_block1(void) { struct CSlul *ctx = create_ctx(CSLUL_P_IMPL); expect_error(CSLUL_E_NORETURNNEWLINE, 6, 12); expect_error(CSLUL_E_NOIN, 3, 14); TEST_SOURCE("func f()\n" "{\n" " for int i x {\n" /* missing "in" */ " return return\n" /* bad syntax, but skipped */ " }\n" " return 123\n" /* recovered, and this gives an error again */ "}\n", 1); free_assert(ctx); } static void test_parse_funcbody_skip_block2(void) { struct CSlul *ctx = create_ctx(CSLUL_P_IMPL); expect_error(CSLUL_E_NORETURNNEWLINE, 6, 12); expect_error(CSLUL_E_INCOMPLETEEXPR, 3, 8); TEST_SOURCE("func f()\n" "{\n" " if {\n" /* missing expression */ " return return\n" /* bad syntax, but skipped */ " }\n" " return 123\n" /* recovered, and this gives an error again */ "}\n", 1); free_assert(ctx); } static void test_parse_funcbody_skip_block3(void) { struct CSlul *ctx = create_ctx(CSLUL_P_IMPL); expect_error(CSLUL_E_NORETURNNEWLINE, 4, 12); expect_error(CSLUL_E_INCOMPLETEEXPR, 3, 8); TEST_SOURCE("func f()\n" "{\n" " if break\n" /* missing expression */ " return 123\n" /* recovered, and this gives an error again */ "}\n", 1); free_assert(ctx); } static void test_parse_funcbody_skip_block4(void) { struct CSlul *ctx = create_ctx(CSLUL_P_IMPL); expect_error(CSLUL_E_CASEOUTSIDESWITCH, 4, 5); expect_error(CSLUL_E_INCOMPLETEEXPR, 3, 8); TEST_SOURCE("func f() -> int\n" "{\n" " if return 1\n" /* missing "if" expression */ " case\n" /* recovered, and this gives an error again */ " return 123\n" "}\n", 1); free_assert(ctx); } static void test_parse_funcbody_skip_block5(void) { struct CSlul *ctx = create_ctx(CSLUL_P_IMPL); expect_error(CSLUL_E_CASEOUTSIDESWITCH, 9, 5); expect_error(CSLUL_E_INCOMPLETEEXPR, 3, 13); TEST_SOURCE("func f()\n" "{\n" " if 1 == {\n" /* incomplete "if" expression */ " {\n" " return return\n" /* bad syntax, but skipped */ " }\n" " return return\n" " }\n" " case\n" /* recovered, and this gives an error again */ "}\n", 1); free_assert(ctx); } static void test_parse_funcbody_skip_stmt1(void) { struct CSlul *ctx = create_ctx(CSLUL_P_IMPL); expect_error(CSLUL_E_CASEOUTSIDESWITCH, 4, 5); expect_error(CSLUL_E_MISSINGOPERAND, 3, 20); TEST_SOURCE("func f()\n" "{\n" " var int i = 1 + ]\n" /* bad expression */ " case\n" /* recovered, and this gives an error again */ "}\n", 1); free_assert(ctx); } static void test_parse_funcbody_skip_stmt2(void) { struct CSlul *ctx = create_ctx(CSLUL_P_IMPL); expect_error(CSLUL_E_CASEOUTSIDESWITCH, 5, 5); expect_error(CSLUL_E_INCOMPLETEEXPR, 3, 22); TEST_SOURCE("func f()\n" "{\n" " var int i = 1 + +\n" /* bad expression */ " case\n" /* invalid expr token. this one is ignored */ " case\n" /* recovered, and this gives an error again */ "}\n", 1); free_assert(ctx); } static void test_parse_funcbody_skip_stmt3(void) { struct CSlul *ctx = create_ctx(CSLUL_P_IMPL); expect_error(CSLUL_E_CASEOUTSIDESWITCH, 6, 5); expect_error(CSLUL_E_INCOMPLETEEXPR, 4, 24); TEST_SOURCE("func f()\n" "{\n" " {\n" " var int i = 1 +\n" /* bad expression */ " }\n" " case\n" /* recovered, and this gives an error again */ "}\n", 1); free_assert(ctx); } static void test_parse_funcbody_skip_stmt4(void) { struct CSlul *ctx = create_ctx(CSLUL_P_IMPL); expect_error(CSLUL_E_CASEOUTSIDESWITCH, 6, 5); expect_error(CSLUL_E_OPERATOREXPECTED, 4, 22); TEST_SOURCE("func f()\n" "{\n" " {\n" " var int i = 1 2\n" /* bad expression */ " }\n" " case\n" /* recovered, and this gives an error again */ "}\n", 1); free_assert(ctx); } static void test_parse_funcbody_bad_exprstart1(void) { struct CSlul *ctx = create_ctx(CSLUL_P_IMPL); expect_error(CSLUL_E_BADSTMTLINESTART, 3, 5); TEST_SOURCE("func f()\n" "{\n" " ]\n", 1); free_assert(ctx); } static void test_parse_funcbody_bad_exprstart2(void) { struct CSlul *ctx = create_ctx(CSLUL_P_IMPL); expect_error(CSLUL_E_OPERATORNOTEXP, 3, 5); TEST_SOURCE("func f()\n" "{\n" " =\n", 1); free_assert(ctx); } static void test_parse_funcbody_bad_exprstart3(void) { struct CSlul *ctx = create_ctx(CSLUL_P_IMPL); expect_error(CSLUL_E_NOCURLYSTMT, 3, 13); TEST_SOURCE("func f()\n" "{\n" " if true }\n" "}\n", 1); free_assert(ctx); } static void test_parse_funcbody_missing_rcurly(void) { struct CSlul *ctx = create_ctx(CSLUL_P_IMPL); expect_error(CSLUL_E_NOFUNCENDCURLY, 3, 1); TEST_SOURCE("func f()\n" "{\n" "\n" "func ", 0); tsoftassert(ctx->current_block == NULL); free_assert(ctx); } static void test_parse_funcbody_toodeep(void) { struct CSlul *ctx = create_ctx(CSLUL_P_IMPL); static const char prefix[] = "func f()\n"; static const char postfix[] = "{}\n" "int i\n" "}\n"; /* 1 extra byte is needed because TEST_SOURCE expects that */ char buff[sizeof(prefix)-1+MAXBLOCKDEPTH+sizeof(postfix)-1+1]; expect_error(CSLUL_E_BLOCKTOODEEP, 2, MAXBLOCKDEPTH+1); memcpy(buff, prefix, sizeof(prefix)-1); memset(buff+sizeof(prefix)-1, '{', MAXBLOCKDEPTH); memcpy(buff+sizeof(prefix)-1+MAXBLOCKDEPTH, postfix, sizeof(postfix)-1); TEST_SOURCE(buff, 1); free_ctx(ctx); } static void test_parse_funcbody_this(void) { struct CSlul *ctx = create_ctx(CSLUL_P_IMPL); struct TypeDecl *t; struct IdentDecl *f; struct FuncBody *fb; struct Stmt *s; struct ExprNode *rpn; TEST_SOURCE("func Thing.f() -> ref Thing\n" "{\n" " return this\n" "}\n", 1); t = lookup_type(ctx, "Thing"); tassert(t != NULL); tassert(t->typeidents != NULL); f = (struct IdentDecl *)lookup(ctx, t->typeidents, "f"); tassert(f != NULL); tassert(f->type.type == T_METHOD); ASSERT_LINECOL(&f->ident, 1, 12); tassert((fb = f->u.funcbody) != NULL); tsoftassert(fb->stmtblock.base == NULL); tsoftassert(fb->stmtblock.idents == NULL); s = &fb->stmtblock.stmt; /* "return this" */ ASSERT_STMT_RETURN(s); tsoftassert(s->next == NULL); tassert(s->u.expr != NULL); tassert(s->u.expr->root != NULL); rpn = s->u.expr->rpn; ASSERT_RPN_THIS(t); ASSERT_RPN_END; tsoftassert(fb->next == NULL); free_assert(ctx); } static void test_parse_funcbody_badthis(void) { struct CSlul *ctx = create_ctx(CSLUL_P_IMPL); expect_error(CSLUL_E_THISOUTSIDEMETHOD, 3, 12); TEST_SOURCE("func f() -> ref Thing\n" "{\n" " return this\n" "}\n", 1); tsoftassert(ctx->current_block == NULL); free_assert(ctx); } static void test_parse_forwardref_ident(void) { struct CSlul *ctx = create_ctx(CSLUL_P_IMPL); struct TypeDecl *te; struct IdentDecl *ea, *c; struct ExprNode *enumexpr; TEST_SOURCE( "type E = enum int {\n" " a = c\n" "}\n" "data byte c = 123\n", 1); /* Check the enum */ te = lookup_type(ctx, "E"); tassert(te != NULL); tassert(te->type.type == T_ENUM); tassert(te->type.u.enu != NULL); ASSERT_TYPE_INT(&te->type.u.enu->base); tassert(te->type.u.enu->values != NULL); tsoftassert(te->type.u.enu->values->next == NULL); ea = &te->type.u.enu->values->e.vd.decl; tassert(ea->u.initval != NULL); tassert(ea->u.initval->root != NULL); tsoftassert((enumexpr = ea->u.initval->root) != NULL); /* Check the data item */ c = lookup_toplevel(ctx, "c"); tassert(c != NULL); tassert(c->type.type == T_ELMNTRY); tassert(c->type.u.builtin == BT_Byte); /* The enum value should reference the data item */ tassert(enumexpr->exprtype == E_IDENT); tsoftassert(enumexpr->a.ident == &c->ident); free_ctx(ctx); } static void test_parse_forwardref_ident_versioned(void) { struct CSlul *ctx = create_ctx(CSLUL_P_IFACE); struct TypeDecl *te; struct IdentDecl *ea, *c; struct ExprNode *enumexpr; TEST_SOURCE( "since 1.0\n" "type E = enum int {\n" " a = c\n" "}\n" "since 1.0\n" "data byte c = 123\n", 1); /* Check the enum */ te = lookup_type_ver(ctx, "E", "1.0"); tassert(te != NULL); tassert(te->type.type == T_ENUM); tassert(te->type.u.enu != NULL); if (tsoftassert(te->type.u.enu->base.type == T_ELMNTRY)) { tsoftassert(te->type.u.enu->base.u.builtin == BT_Int); } tassert(te->type.u.enu->values != NULL); tsoftassert(te->type.u.enu->values->next == NULL); ea = &te->type.u.enu->values->e.vd.decl; tassert(ea->u.initval != NULL); tassert(ea->u.initval->root != NULL); tsoftassert((enumexpr = ea->u.initval->root) != NULL); /* Check the data item */ c = lookup_toplevel_ver(ctx, "c", "1.0"); tassert(c != NULL); tassert(c->type.type == T_ELMNTRY); tassert(c->type.u.builtin == BT_Byte); /* The enum value should reference the data item */ tassert(enumexpr->exprtype == E_IDENT); tsoftassert(enumexpr->a.ident == &c->ident); free_ctx(ctx); } static void test_parse_forwardref_type(void) { struct CSlul *ctx = create_ctx(CSLUL_P_IMPL); struct TypeDecl *t, *u; struct IdentDecl *x; TEST_SOURCE( "type T = struct {\n" " ref U x\n" "}\n" "type U = struct {\n" " int y\n" "}\n", 1); t = lookup_type(ctx, "T"); tassert(t != NULL); tassert(t->type.type == T_STRUCT); tassert(t->type.u.fields != NULL); tassert(t->type.u.fields->count == 1); tassert(t->type.u.fields->first != NULL); x = &t->type.u.fields->first->f.vardef.decl; tassert(lookup(ctx, t->type.u.fields->fields_root, "x") == &x->ident); tassert(x->type.type == T_REF); tassert(x->type.u.nested != NULL); tassert(x->type.u.nested->type == T_IDENT); u = lookup_type(ctx, "U"); tassert(u != NULL); tassert(u->type.type == T_STRUCT); tassert(x->type.u.nested->u.ident == u); free_ctx(ctx); } static void test_parse_forwardref_type_versioned(void) { struct CSlul *ctx = create_ctx(CSLUL_P_IFACE); struct TypeDecl *t, *u; struct IdentDecl *x; TEST_SOURCE( "since 1.0\n" "type T = struct {\n" " ref U x\n" "}\n" "since 1.0\n" "type U = struct {\n" " int y\n" "}\n", 1); t = lookup_type_ver(ctx, "T", "1.0"); tassert(t != NULL); tassert(t->type.type == T_STRUCT); tassert(t->type.u.fields != NULL); tassert(t->type.u.fields->count == 1); tassert(t->type.u.fields->first != NULL); x = &t->type.u.fields->first->f.vardef.decl; tassert(lookup(ctx, t->type.u.fields->fields_root, "x") == &x->ident); tassert(x->type.type == T_REF); tassert(x->type.u.nested != NULL); tassert(x->type.u.nested->type == T_IDENT); u = lookup_type_ver(ctx, "U", "1.0"); tassert(u != NULL); tassert(u->type.type == T_STRUCT); tassert(x->type.u.nested->u.ident == u); free_ctx(ctx); } static void test_parse_identcontexts1(void) { struct CSlul *ctx = create_ctx(CSLUL_P_IMPL); struct IdentDecl *f, *gv; struct TreeNode *param; struct ExprNode *expr; TEST_SOURCE( "func f(int x) -> int {\n" " return x\n" /* reference to the parameter */ "}\n" "data int x = 123\n", 1); f = lookup_toplevel(ctx, "f"); tassert(f != NULL); tassert(f->u.funcbody != NULL); tassert(f->u.funcbody->stmtblock.stmt.type == S_RETURN); tassert(f->u.funcbody->stmtblock.stmt.u.expr != NULL); expr = f->u.funcbody->stmtblock.stmt.u.expr->root; tassert(expr != NULL); tassert(expr->exprtype == E_IDENT); param = lookup(ctx, f->type.u.fields->fields_root, "x"); tsoftassert(param != NULL); tassert(expr->a.ident == param); gv = lookup_toplevel(ctx, "x"); tassert(gv != NULL); tassert(expr->a.ident != &gv->ident); /* not the same! */ free_ctx(ctx); } static void test_parse_identcontexts2(void) { struct CSlul *ctx = create_ctx(CSLUL_P_IMPL); struct IdentDecl *f, *gv; struct ExprNode *expr; TEST_SOURCE( "func f() -> int {\n" " return x\n" /* reference to the global data item */ "}\n" "data int x = 123\n", 1); f = lookup_toplevel(ctx, "f"); tassert(f != NULL); tassert(f->u.funcbody != NULL); tassert(f->u.funcbody->stmtblock.stmt.type == S_RETURN); tassert(f->u.funcbody->stmtblock.stmt.u.expr != NULL); expr = f->u.funcbody->stmtblock.stmt.u.expr->root; tassert(expr != NULL); tassert(expr->exprtype == E_IDENT); gv = lookup_toplevel(ctx, "x"); tassert(gv != NULL); tassert(expr->a.ident == &gv->ident); free_ctx(ctx); } const TestFunctionInfo tests_parse[] = { TEST_BEFORE(before_test) TEST_AFTER(after_test) TEST_INFO(test_parse_ast_constants) TEST_INFO(test_parse_declsize) TEST_INFO(test_parse_toplevel_data) TEST_INFO(test_parse_toplevel_data_initval) TEST_INFO(test_parse_toplevel_data_versioned) TEST_INFO(test_parse_toplevel_data_dupl1) TEST_INFO(test_parse_toplevel_data_dupl2) TEST_INFO(test_parse_toplevel_data_badtype) TEST_INFO(test_parse_toplevel_data_noident) TEST_INFO(test_parse_toplevel_data_noequals) TEST_INFO(test_parse_toplevel_data_badcase1) TEST_INFO(test_parse_toplevel_data_badqual1) TEST_INFO(test_parse_toplevel_data_badqual2) TEST_INFO(test_parse_toplevel_data_badref) TEST_INFO(test_parse_toplevel_data_missinginitval1) TEST_INFO(test_parse_toplevel_data_missinginitval2) TEST_INFO(test_parse_toplevel_data_missingsince) TEST_INFO(test_parse_toplevel_data_badversion1) TEST_INFO(test_parse_toplevel_data_badversion2) TEST_INFO(test_parse_toplevel_data_versionwithoutapidef) TEST_INFO(test_parse_toplevel_data_versioninimpl) TEST_INFO(test_parse_toplevel_data_nosuchversion) TEST_INFO(test_parse_toplevel_data_sincecurly) TEST_INFO(test_parse_toplevel_bad_start) TEST_INFO(test_parse_toplevel_bad_column) TEST_INFO(test_parse_toplevel_type) TEST_INFO(test_parse_toplevel_type_dupl) TEST_INFO(test_parse_toplevel_type_noident) TEST_INFO(test_parse_toplevel_type_noequals) TEST_INFO(test_parse_toplevel_type_badtype) TEST_INFO(test_parse_toplevel_type_badqual) TEST_INFO(test_parse_type_ref) TEST_INFO(test_parse_type_ref_optional) TEST_INFO(test_parse_type_optional_bad) TEST_INFO(test_parse_toplevel_type_eof1) TEST_INFO(test_parse_toplevel_type_eof2) TEST_INFO(test_parse_type_array_largelength) TEST_INFO(test_parse_type_array_noexpr) TEST_INFO(test_parse_type_array_noendbracket) TEST_INFO(test_parse_type_array_noelemtype) TEST_INFO(test_parse_type_array_multidim) TEST_INFO(test_parse_type_array_multidim_empty1) TEST_INFO(test_parse_type_array_multidim_empty2) TEST_INFO(test_parse_type_array_multidim_empty3) TEST_INFO(test_parse_type_ident) TEST_INFO(test_parse_type_private) TEST_INFO(test_parse_type_private_notiface1) TEST_INFO(test_parse_type_private_notiface2) TEST_INFO(test_parse_type_struct) TEST_INFO(test_parse_type_struct_versioned) TEST_INFO(test_parse_type_struct_nested) TEST_INFO(test_parse_type_struct_duplfield) TEST_INFO(test_parse_type_struct_badsemicolon) TEST_INFO(test_parse_type_enum) TEST_INFO(test_parse_type_enum_versioned) TEST_INFO(test_parse_type_enum_duplident) TEST_INFO(test_parse_type_enum_wrongorder) TEST_INFO(test_parse_type_enum_closed_version) TEST_INFO(test_parse_type_enum_badcomma) TEST_INFO(test_parse_type_enum_badquals) TEST_INFO(test_parse_type_func_noargs) TEST_INFO(test_parse_type_quals) TEST_INFO(test_parse_type_quals_duplicated) TEST_INFO(test_parse_type_quals_implclosed) TEST_INFO(test_parse_type_generic_use) TEST_INFO(test_parse_type_generic_use_noparams1) TEST_INFO(test_parse_type_generic_use_noparams2) TEST_INFO(test_parse_type_generic_use_trailingcomma) TEST_INFO(test_parse_type_generic_use_missingend) TEST_INFO(test_parse_type_generic_use_2params) TEST_INFO(test_parse_type_generic_use_toomany) TEST_INFO(test_parse_type_generic_define) TEST_INFO(test_parse_type_generic_define_toomany) TEST_INFO(test_parse_type_generic_define_dupl) TEST_INFO(test_parse_type_generic_define_empty1) TEST_INFO(test_parse_type_generic_define_empty2) TEST_INFO(test_parse_type_generic_define_badtype) TEST_INFO(test_parse_type_generic_define_badend) TEST_INFO(test_parse_type_versioned) TEST_INFO(test_parse_type_versioned_multiple) TEST_INFO(test_parse_type_versionref) TEST_INFO(test_parse_type_badcase1) TEST_INFO(test_parse_type_miscbad) TEST_INFO(test_parse_type_toodeep_struct) TEST_INFO(test_parse_type_toodeep_generic) TEST_INFO(test_parse_type_enum_nested) TEST_INFO(test_parse_func_noargs) TEST_INFO(test_parse_func_noreturn) TEST_INFO(test_parse_func_1arg) TEST_INFO(test_parse_func_2args) TEST_INFO(test_parse_func_duplparam) TEST_INFO(test_parse_func_badquals) TEST_INFO(test_parse_func_missingfuncbody) TEST_INFO(test_parse_func_versioned) TEST_INFO(test_parse_func_missingversion1) TEST_INFO(test_parse_func_missingversion2) TEST_INFO(test_parse_func_missingversion3) TEST_INFO(test_parse_func_missingversion4) TEST_INFO(test_parse_func_unversioned) TEST_INFO(test_parse_func_nosuchversion) TEST_INFO(test_parse_func_emptybody) TEST_INFO(test_parse_func_generic) TEST_INFO(test_parse_func_lifetime_good1) TEST_INFO(test_parse_func_lifetime_good2) TEST_INFO(test_parse_func_lifetime_good_multiple) TEST_INFO(test_parse_func_lifetime_truncated) TEST_INFO(test_parse_func_lifetime_badident1) TEST_INFO(test_parse_func_lifetime_badrel) TEST_INFO(test_parse_func_lifetime_badident2) TEST_INFO(test_parse_func_lifetime_dup_return) TEST_INFO(test_parse_func_lifetime_dup_param) TEST_INFO(test_parse_func_lifetime_nonexisting1) TEST_INFO(test_parse_func_lifetime_nonexisting2) TEST_INFO(test_parse_func_lifetime_nonref) TEST_INFO(test_parse_typeident_data) TEST_INFO(test_parse_typeident_data_versioned) TEST_INFO(test_parse_typeident_data_badeof) TEST_INFO(test_parse_typeident_data_notappl) TEST_INFO(test_parse_typeident_data_dupl) TEST_INFO(test_parse_typeident_data_verdupl) TEST_INFO(test_parse_typeident_data_vermix1) TEST_INFO(test_parse_typeident_data_vermix2) TEST_INFO(test_parse_typeident_func_nonref) TEST_INFO(test_parse_typeident_func_ref) TEST_INFO(test_parse_typeident_func_ref_versioned) TEST_INFO(test_parse_typeident_func_parametric_versioned) TEST_INFO(test_parse_typeident_func_badeof1) TEST_INFO(test_parse_typeident_func_badeof2) TEST_INFO(test_parse_typeident_func_notappl) TEST_INFO(test_parse_typeident_func_dupl) TEST_INFO(test_parse_typeident_func_verdupl) TEST_INFO(test_parse_typeident_func_vermix1) TEST_INFO(test_parse_typeident_func_vermix2) TEST_INFO(test_parse_method) TEST_INFO(test_parse_method_versioned) TEST_INFO(test_parse_method_parametric) TEST_INFO(test_parse_method_parametric_versioned) TEST_INFO(test_parse_method_badeof1) TEST_INFO(test_parse_method_badeof2) TEST_INFO(test_parse_method_badsymbol) TEST_INFO(test_parse_method_dupl) TEST_INFO(test_parse_method_dupl_ver) TEST_INFO(test_parse_expr_empty1) TEST_INFO(test_parse_expr_empty2) TEST_INFO(test_parse_expr_bad_eof) TEST_INFO(test_parse_expr_terminal) TEST_INFO(test_parse_expr_typeident) TEST_INFO(test_parse_expr_bad_dot) TEST_INFO(test_parse_expr_bad_exclamation) TEST_INFO(test_parse_expr_field) TEST_INFO(test_parse_expr_prefix) TEST_INFO(test_parse_expr_prefix_nested) TEST_INFO(test_parse_expr_prefix_unexpected) TEST_INFO(test_parse_expr_binary_nested) TEST_INFO(test_parse_expr_binary_parens) TEST_INFO(test_parse_expr_array) TEST_INFO(test_parse_expr_array_nested) TEST_INFO(test_parse_expr_array_trailingcomma) TEST_INFO(test_parse_expr_array_noend) TEST_INFO(test_parse_expr_array_badeof) TEST_INFO(test_parse_expr_string_empty) TEST_INFO(test_parse_expr_string_single) TEST_INFO(test_parse_expr_string_2chunks) TEST_INFO(test_parse_expr_string_5chunks) TEST_INFO(test_parse_expr_ambiguous_mix) TEST_INFO(test_parse_expr_unclosed_paren) TEST_INFO(test_parse_expr_mismatched_paren1) TEST_INFO(test_parse_expr_mismatched_paren2) TEST_INFO(test_parse_expr_mismatched_paren3) TEST_INFO(test_parse_expr_unexpected_operator) TEST_INFO(test_parse_expr_missing_operand1) TEST_INFO(test_parse_expr_missing_operand2) TEST_INFO(test_parse_expr_missing_operand3) TEST_INFO(test_parse_expr_missing_operand4) TEST_INFO(test_parse_expr_missing_operand5) TEST_INFO(test_parse_expr_missing_operand6) TEST_INFO(test_parse_expr_missing_operand7) TEST_INFO(test_parse_expr_missing_operator) TEST_INFO(test_parse_expr_bad_token1) TEST_INFO(test_parse_expr_bad_token2) TEST_INFO(test_parse_expr_bad_semicolon) TEST_INFO(test_parse_opinfo) TEST_INFO(test_parse_funcbody_empty) TEST_INFO(test_parse_funcbody_expr1) TEST_INFO(test_parse_funcbody_expr2) TEST_INFO(test_parse_funcbody_returnvoid) TEST_INFO(test_parse_funcbody_recovery1) TEST_INFO(test_parse_funcbody_returnexpr) TEST_INFO(test_parse_funcbody_assert) TEST_INFO(test_parse_funcbody_block) TEST_INFO(test_parse_funcbody_if) TEST_INFO(test_parse_funcbody_else) TEST_INFO(test_parse_funcbody_else_noif1) TEST_INFO(test_parse_funcbody_else_noif2) TEST_INFO(test_parse_funcbody_else_noif3) TEST_INFO(test_parse_funcbody_while) TEST_INFO(test_parse_funcbody_dowhile) TEST_INFO(test_parse_funcbody_dowhile_nowhile1) TEST_INFO(test_parse_funcbody_dowhile_nowhile2) TEST_INFO(test_parse_funcbody_dowhile_nobraces) TEST_INFO(test_parse_funcbody_for_notype1) TEST_INFO(test_parse_funcbody_for_noident1) TEST_INFO(test_parse_funcbody_for_noin1) TEST_INFO(test_parse_funcbody_for_noexpr1) TEST_INFO(test_parse_funcbody_for_badqual) TEST_INFO(test_parse_funcbody_breakcontinue) TEST_INFO(test_parse_funcbody_breakcontinue_noloop1) TEST_INFO(test_parse_funcbody_breakcontinue_noloop2) TEST_INFO(test_parse_funcbody_breakcontinue_noloop3) TEST_INFO(test_parse_funcbody_loopempty) TEST_INFO(test_parse_funcbody_loopend) TEST_INFO(test_parse_funcbody_loopend_without_break) TEST_INFO(test_parse_funcbody_loopemptyend_dowhile) TEST_INFO(test_parse_funcbody_loopemptyend_for) TEST_INFO(test_parse_funcbody_loopemptyend_singlestmt) TEST_INFO(test_parse_funcbody_loopemptyend_scope) TEST_INFO(test_parse_funcbody_loopemptyend_wrongorder) TEST_INFO(test_parse_funcbody_loopemptyend_badbreak) TEST_INFO(test_parse_funcbody_loopempty_duplicate) TEST_INFO(test_parse_funcbody_loopend_duplicate) TEST_INFO(test_parse_funcbody_goto_forward) TEST_INFO(test_parse_funcbody_goto_backward) TEST_INFO(test_parse_funcbody_goto_duplicate) TEST_INFO(test_parse_funcbody_goto_missing) TEST_INFO(test_parse_funcbody_switch_default) TEST_INFO(test_parse_funcbody_switch_case) TEST_INFO(test_parse_funcbody_switch_case2val) TEST_INFO(test_parse_funcbody_switch_multiple) TEST_INFO(test_parse_funcbody_switch_subcase) TEST_INFO(test_parse_funcbody_switch_nolcurly) TEST_INFO(test_parse_funcbody_switch_nocases) TEST_INFO(test_parse_funcbody_switch_nodefaultcolon) TEST_INFO(test_parse_funcbody_switch_nocasecolon) TEST_INFO(test_parse_funcbody_switch_nocaseexpr) TEST_INFO(test_parse_funcbody_switch_badcaseexpr1) TEST_INFO(test_parse_funcbody_switch_badcaseexpr2) TEST_INFO(test_parse_funcbody_switch_repeatedcase) TEST_INFO(test_parse_funcbody_switch_badsubcase1) TEST_INFO(test_parse_funcbody_switch_badsubcase2) TEST_INFO(test_parse_funcbody_vardef_noinit) TEST_INFO(test_parse_funcbody_vardef_init) TEST_INFO(test_parse_funcbody_vardef_nested) TEST_INFO(test_parse_funcbody_vardef_outofscope) TEST_INFO(test_parse_funcbody_vardef_missingident) /* TODO tests of vardef expr parsing end point / start token of following line */ TEST_INFO(test_parse_funcbody_nonewline_return) TEST_INFO(test_parse_funcbody_nonewline_rcurly) TEST_INFO(test_parse_funcbody_nonewline_expr1) TEST_INFO(test_parse_funcbody_nonewline_expr2) TEST_INFO(test_parse_funcbody_nonewline_expr3) TEST_INFO(test_parse_funcbody_mismatched_rcurly) TEST_INFO(test_parse_funcbody_skip_block1) TEST_INFO(test_parse_funcbody_skip_block2) TEST_INFO(test_parse_funcbody_skip_block3) TEST_INFO(test_parse_funcbody_skip_block4) TEST_INFO(test_parse_funcbody_skip_block5) TEST_INFO(test_parse_funcbody_skip_stmt1) TEST_INFO(test_parse_funcbody_skip_stmt2) TEST_INFO(test_parse_funcbody_skip_stmt3) TEST_INFO(test_parse_funcbody_skip_stmt4) TEST_INFO(test_parse_funcbody_bad_exprstart1) TEST_INFO(test_parse_funcbody_bad_exprstart2) TEST_INFO(test_parse_funcbody_bad_exprstart3) TEST_INFO(test_parse_funcbody_missing_rcurly) TEST_INFO(test_parse_funcbody_toodeep) TEST_INFO(test_parse_funcbody_this) TEST_INFO(test_parse_funcbody_badthis) TEST_INFO(test_parse_forwardref_ident) TEST_INFO(test_parse_forwardref_ident_versioned) TEST_INFO(test_parse_forwardref_type) TEST_INFO(test_parse_forwardref_type_versioned) TEST_INFO(test_parse_identcontexts1) TEST_INFO(test_parse_identcontexts2) TEST_END };