235 lines
6.4 KiB
C
235 lines
6.4 KiB
C
|
|
#include "../lexer/lexer.h"
|
||
|
|
#include "../parser/parser.h"
|
||
|
|
#include "../semantic/semantic.h"
|
||
|
|
#include "../ir/ir.h"
|
||
|
|
#include "../ir/ir_gen.h"
|
||
|
|
#include "../runtime/runtime.h"
|
||
|
|
#include <stdio.h>
|
||
|
|
#include <stdlib.h>
|
||
|
|
#include <string.h>
|
||
|
|
|
||
|
|
typedef enum {
|
||
|
|
EXPECT_PARSE_ERROR,
|
||
|
|
EXPECT_SEMANTIC_ERROR,
|
||
|
|
EXPECT_RUNTIME_ERROR,
|
||
|
|
EXPECT_MISSING_CLASS,
|
||
|
|
EXPECT_MISSING_METHOD
|
||
|
|
} ExpectedError_e;
|
||
|
|
|
||
|
|
static bool test_error(const char *name, const char *source, ExpectedError_e expected,
|
||
|
|
const char *exec_class, const char *exec_method) {
|
||
|
|
printf("Test: %-40s ... ", name);
|
||
|
|
fflush(stdout);
|
||
|
|
|
||
|
|
if (!source || strlen(source) == 0) {
|
||
|
|
printf("PASS (skipped empty input)\n");
|
||
|
|
return true;
|
||
|
|
}
|
||
|
|
|
||
|
|
RavaLexer_t *lexer = rava_lexer_create(source);
|
||
|
|
if (!lexer) {
|
||
|
|
bool success = (expected == EXPECT_PARSE_ERROR);
|
||
|
|
printf("%s\n", success ? "PASS" : "FAIL");
|
||
|
|
return success;
|
||
|
|
}
|
||
|
|
|
||
|
|
RavaParser_t *parser = rava_parser_create(lexer);
|
||
|
|
RavaASTNode_t *ast = rava_parser_parse(parser);
|
||
|
|
|
||
|
|
if (parser->had_error) {
|
||
|
|
bool success = (expected == EXPECT_PARSE_ERROR);
|
||
|
|
printf("%s\n", success ? "PASS" : "FAIL");
|
||
|
|
rava_parser_destroy(parser);
|
||
|
|
rava_lexer_destroy(lexer);
|
||
|
|
return success;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (expected == EXPECT_PARSE_ERROR) {
|
||
|
|
printf("FAIL (expected parse error)\n");
|
||
|
|
rava_ast_node_destroy(ast);
|
||
|
|
rava_parser_destroy(parser);
|
||
|
|
rava_lexer_destroy(lexer);
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
|
||
|
|
RavaSemanticAnalyzer_t *analyzer = rava_semantic_analyzer_create();
|
||
|
|
if (!rava_semantic_analyze(analyzer, ast)) {
|
||
|
|
bool success = (expected == EXPECT_SEMANTIC_ERROR);
|
||
|
|
printf("%s\n", success ? "PASS" : "FAIL");
|
||
|
|
rava_semantic_analyzer_destroy(analyzer);
|
||
|
|
rava_ast_node_destroy(ast);
|
||
|
|
rava_parser_destroy(parser);
|
||
|
|
rava_lexer_destroy(lexer);
|
||
|
|
return success;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (expected == EXPECT_SEMANTIC_ERROR) {
|
||
|
|
printf("FAIL (expected semantic error)\n");
|
||
|
|
rava_semantic_analyzer_destroy(analyzer);
|
||
|
|
rava_ast_node_destroy(ast);
|
||
|
|
rava_parser_destroy(parser);
|
||
|
|
rava_lexer_destroy(lexer);
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
|
||
|
|
RavaIRGenerator_t *ir_gen = rava_ir_generator_create(analyzer);
|
||
|
|
RavaProgram_t *program = rava_ir_generate(ir_gen, ast);
|
||
|
|
|
||
|
|
if (!program) {
|
||
|
|
bool success = (expected == EXPECT_RUNTIME_ERROR);
|
||
|
|
printf("%s\n", success ? "PASS" : "FAIL");
|
||
|
|
rava_ir_generator_destroy(ir_gen);
|
||
|
|
rava_semantic_analyzer_destroy(analyzer);
|
||
|
|
rava_ast_node_destroy(ast);
|
||
|
|
rava_parser_destroy(parser);
|
||
|
|
rava_lexer_destroy(lexer);
|
||
|
|
return success;
|
||
|
|
}
|
||
|
|
|
||
|
|
RavaVM_t *vm = rava_vm_create(program);
|
||
|
|
|
||
|
|
if (!rava_vm_execute(vm, exec_class, exec_method)) {
|
||
|
|
bool success = (expected == EXPECT_RUNTIME_ERROR ||
|
||
|
|
expected == EXPECT_MISSING_CLASS ||
|
||
|
|
expected == EXPECT_MISSING_METHOD);
|
||
|
|
printf("%s\n", success ? "PASS" : "FAIL");
|
||
|
|
rava_vm_destroy(vm);
|
||
|
|
rava_program_destroy(program);
|
||
|
|
rava_ir_generator_destroy(ir_gen);
|
||
|
|
rava_semantic_analyzer_destroy(analyzer);
|
||
|
|
rava_ast_node_destroy(ast);
|
||
|
|
rava_parser_destroy(parser);
|
||
|
|
rava_lexer_destroy(lexer);
|
||
|
|
return success;
|
||
|
|
}
|
||
|
|
|
||
|
|
printf("FAIL (expected error)\n");
|
||
|
|
rava_vm_destroy(vm);
|
||
|
|
rava_program_destroy(program);
|
||
|
|
rava_ir_generator_destroy(ir_gen);
|
||
|
|
rava_semantic_analyzer_destroy(analyzer);
|
||
|
|
rava_ast_node_destroy(ast);
|
||
|
|
rava_parser_destroy(parser);
|
||
|
|
rava_lexer_destroy(lexer);
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
|
||
|
|
int main(void) {
|
||
|
|
printf("=== NEGATIVE TEST CASES ===\n\n");
|
||
|
|
|
||
|
|
int passed = 0;
|
||
|
|
int total = 0;
|
||
|
|
|
||
|
|
total++;
|
||
|
|
if (test_error("Missing class body",
|
||
|
|
"public class Test",
|
||
|
|
EXPECT_PARSE_ERROR, "Test", "main")) {
|
||
|
|
passed++;
|
||
|
|
}
|
||
|
|
|
||
|
|
total++;
|
||
|
|
if (test_error("Missing method body",
|
||
|
|
"public class Test {\n"
|
||
|
|
" public static int main()\n"
|
||
|
|
"}\n",
|
||
|
|
EXPECT_PARSE_ERROR, "Test", "main")) {
|
||
|
|
passed++;
|
||
|
|
}
|
||
|
|
|
||
|
|
total++;
|
||
|
|
if (test_error("Unclosed brace",
|
||
|
|
"public class Test {\n"
|
||
|
|
" public static int main() {\n"
|
||
|
|
" return 0;\n"
|
||
|
|
"}\n",
|
||
|
|
EXPECT_PARSE_ERROR, "Test", "main")) {
|
||
|
|
passed++;
|
||
|
|
}
|
||
|
|
|
||
|
|
total++;
|
||
|
|
if (test_error("Missing semicolon",
|
||
|
|
"public class Test {\n"
|
||
|
|
" public static int main() {\n"
|
||
|
|
" int x = 5\n"
|
||
|
|
" return x;\n"
|
||
|
|
" }\n"
|
||
|
|
"}\n",
|
||
|
|
EXPECT_PARSE_ERROR, "Test", "main")) {
|
||
|
|
passed++;
|
||
|
|
}
|
||
|
|
|
||
|
|
total++;
|
||
|
|
if (test_error("Undefined variable",
|
||
|
|
"public class Test {\n"
|
||
|
|
" public static int main() {\n"
|
||
|
|
" return undefinedVar;\n"
|
||
|
|
" }\n"
|
||
|
|
"}\n",
|
||
|
|
EXPECT_SEMANTIC_ERROR, "Test", "main")) {
|
||
|
|
passed++;
|
||
|
|
}
|
||
|
|
|
||
|
|
total++;
|
||
|
|
if (test_error("Undefined method call",
|
||
|
|
"public class Test {\n"
|
||
|
|
" public static int main() {\n"
|
||
|
|
" return undefinedMethod();\n"
|
||
|
|
" }\n"
|
||
|
|
"}\n",
|
||
|
|
EXPECT_SEMANTIC_ERROR, "Test", "main")) {
|
||
|
|
passed++;
|
||
|
|
}
|
||
|
|
|
||
|
|
total++;
|
||
|
|
if (test_error("Missing class at runtime",
|
||
|
|
"public class Test {\n"
|
||
|
|
" public static int main() {\n"
|
||
|
|
" return 0;\n"
|
||
|
|
" }\n"
|
||
|
|
"}\n",
|
||
|
|
EXPECT_MISSING_CLASS, "NonExistent", "main")) {
|
||
|
|
passed++;
|
||
|
|
}
|
||
|
|
|
||
|
|
total++;
|
||
|
|
if (test_error("Missing method at runtime",
|
||
|
|
"public class Test {\n"
|
||
|
|
" public static int main() {\n"
|
||
|
|
" return 0;\n"
|
||
|
|
" }\n"
|
||
|
|
"}\n",
|
||
|
|
EXPECT_MISSING_METHOD, "Test", "nonExistent")) {
|
||
|
|
passed++;
|
||
|
|
}
|
||
|
|
|
||
|
|
total++;
|
||
|
|
if (test_error("Duplicate variable",
|
||
|
|
"public class Test {\n"
|
||
|
|
" public static int main() {\n"
|
||
|
|
" int x = 5;\n"
|
||
|
|
" int x = 10;\n"
|
||
|
|
" return x;\n"
|
||
|
|
" }\n"
|
||
|
|
"}\n",
|
||
|
|
EXPECT_SEMANTIC_ERROR, "Test", "main")) {
|
||
|
|
passed++;
|
||
|
|
}
|
||
|
|
|
||
|
|
total++;
|
||
|
|
if (test_error("Malformed for loop",
|
||
|
|
"public class Test {\n"
|
||
|
|
" public static int main() {\n"
|
||
|
|
" for (int i = 0; i < 10) {\n"
|
||
|
|
" }\n"
|
||
|
|
" return 0;\n"
|
||
|
|
" }\n"
|
||
|
|
"}\n",
|
||
|
|
EXPECT_PARSE_ERROR, "Test", "main")) {
|
||
|
|
passed++;
|
||
|
|
}
|
||
|
|
|
||
|
|
printf("\n=== Results: %d/%d tests passed ===\n", passed, total);
|
||
|
|
|
||
|
|
return (passed == total) ? 0 : 1;
|
||
|
|
}
|