diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 8d89d26bfe..b0709f2e64 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -22,3 +22,12 @@ jobs: - name: Run the default task run: bundle exec rake + + test-native: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v2 + + - name: Run native tests + run: make test diff --git a/.gitignore b/.gitignore index a5fbc8cc6c..3f2a9cab90 100644 --- a/.gitignore +++ b/.gitignore @@ -10,3 +10,5 @@ /lib/yarp/yarp.* test.rb +test-native/run-one +*.dSYM diff --git a/Makefile b/Makefile new file mode 100644 index 0000000000..179c194c40 --- /dev/null +++ b/Makefile @@ -0,0 +1,15 @@ +CFLAGS += -fsanitize=address -g +CFLAGS += -Wall + +LSAN_OPTIONS = suppressions=test-native/LSan.supp:print_suppressions=0 + +test: test-native/run-one + LSAN_OPTIONS=$(LSAN_OPTIONS) ASAN_OPTIONS=detect_leaks=1 ./test-native/run-all.sh + +test-native/run-one: test-native/run-one.c + $(CC) $(CFLAGS) $< -o $@ + +clean: + rm -f test-native/run-one + +.PHONY: test clean diff --git a/bin/templates/token_type.c.erb b/bin/templates/token_type.c.erb index f45b5d7c33..c94459fcec 100644 --- a/bin/templates/token_type.c.erb +++ b/bin/templates/token_type.c.erb @@ -1,4 +1,5 @@ #include "token_type.h" +#include const char * token_type_to_str(yp_token_type_t token_type) @@ -12,3 +13,16 @@ token_type_to_str(yp_token_type_t token_type) return "MAXIMUM"; } } + +yp_token_type_t +token_type_from_str(const char *s) +{ +<%- tokens.each do |token| -%> + if (strcmp(s, "<%= token.name %>") == 0) { + return YP_TOKEN_<%= token.name %>; + } +<%- end -%> + + // Fallback + return YP_TOKEN_INVALID; +} diff --git a/bin/templates/token_type.h.erb b/bin/templates/token_type.h.erb index f3c66cbc09..556bec2ee2 100644 --- a/bin/templates/token_type.h.erb +++ b/bin/templates/token_type.h.erb @@ -9,5 +9,6 @@ typedef enum yp_token_type { } yp_token_type_t; const char *token_type_to_str(yp_token_type_t token_type); +yp_token_type_t token_type_from_str(const char *s); #endif // YARP_TOKEN_TYPE_H diff --git a/ext/yarp/token_type.c b/ext/yarp/token_type.c index 1a2e695ec2..1717150de4 100644 --- a/ext/yarp/token_type.c +++ b/ext/yarp/token_type.c @@ -1,4 +1,5 @@ #include "token_type.h" +#include const char * token_type_to_str(yp_token_type_t token_type) { @@ -281,3 +282,424 @@ token_type_to_str(yp_token_type_t token_type) { return "MAXIMUM"; } } + +yp_token_type_t +token_type_from_str(const char *s) { + if (strcmp(s, "EOF") == 0) { + return YP_TOKEN_EOF; + } + if (strcmp(s, "INVALID") == 0) { + return YP_TOKEN_INVALID; + } + if (strcmp(s, "AMPERSAND") == 0) { + return YP_TOKEN_AMPERSAND; + } + if (strcmp(s, "AMPERSAND_AMPERSAND") == 0) { + return YP_TOKEN_AMPERSAND_AMPERSAND; + } + if (strcmp(s, "AMPERSAND_AMPERSAND_EQUAL") == 0) { + return YP_TOKEN_AMPERSAND_AMPERSAND_EQUAL; + } + if (strcmp(s, "AMPERSAND_EQUAL") == 0) { + return YP_TOKEN_AMPERSAND_EQUAL; + } + if (strcmp(s, "BACK_REFERENCE") == 0) { + return YP_TOKEN_BACK_REFERENCE; + } + if (strcmp(s, "BACKTICK") == 0) { + return YP_TOKEN_BACKTICK; + } + if (strcmp(s, "BANG") == 0) { + return YP_TOKEN_BANG; + } + if (strcmp(s, "BANG_AT") == 0) { + return YP_TOKEN_BANG_AT; + } + if (strcmp(s, "BANG_EQUAL") == 0) { + return YP_TOKEN_BANG_EQUAL; + } + if (strcmp(s, "BANG_TILDE") == 0) { + return YP_TOKEN_BANG_TILDE; + } + if (strcmp(s, "BRACE_LEFT") == 0) { + return YP_TOKEN_BRACE_LEFT; + } + if (strcmp(s, "BRACE_RIGHT") == 0) { + return YP_TOKEN_BRACE_RIGHT; + } + if (strcmp(s, "BRACKET_LEFT") == 0) { + return YP_TOKEN_BRACKET_LEFT; + } + if (strcmp(s, "BRACKET_LEFT_RIGHT") == 0) { + return YP_TOKEN_BRACKET_LEFT_RIGHT; + } + if (strcmp(s, "BRACKET_RIGHT") == 0) { + return YP_TOKEN_BRACKET_RIGHT; + } + if (strcmp(s, "CARET") == 0) { + return YP_TOKEN_CARET; + } + if (strcmp(s, "CARET_EQUAL") == 0) { + return YP_TOKEN_CARET_EQUAL; + } + if (strcmp(s, "CHARACTER_LITERAL") == 0) { + return YP_TOKEN_CHARACTER_LITERAL; + } + if (strcmp(s, "CLASS_VARIABLE") == 0) { + return YP_TOKEN_CLASS_VARIABLE; + } + if (strcmp(s, "COLON") == 0) { + return YP_TOKEN_COLON; + } + if (strcmp(s, "COLON_COLON") == 0) { + return YP_TOKEN_COLON_COLON; + } + if (strcmp(s, "COMMA") == 0) { + return YP_TOKEN_COMMA; + } + if (strcmp(s, "COMMENT") == 0) { + return YP_TOKEN_COMMENT; + } + if (strcmp(s, "CONSTANT") == 0) { + return YP_TOKEN_CONSTANT; + } + if (strcmp(s, "DOT") == 0) { + return YP_TOKEN_DOT; + } + if (strcmp(s, "DOT_DOT") == 0) { + return YP_TOKEN_DOT_DOT; + } + if (strcmp(s, "DOT_DOT_DOT") == 0) { + return YP_TOKEN_DOT_DOT_DOT; + } + if (strcmp(s, "EMBDOC_BEGIN") == 0) { + return YP_TOKEN_EMBDOC_BEGIN; + } + if (strcmp(s, "EMBDOC_END") == 0) { + return YP_TOKEN_EMBDOC_END; + } + if (strcmp(s, "EMBDOC_LINE") == 0) { + return YP_TOKEN_EMBDOC_LINE; + } + if (strcmp(s, "EMBEXPR_BEGIN") == 0) { + return YP_TOKEN_EMBEXPR_BEGIN; + } + if (strcmp(s, "EMBEXPR_END") == 0) { + return YP_TOKEN_EMBEXPR_END; + } + if (strcmp(s, "EQUAL") == 0) { + return YP_TOKEN_EQUAL; + } + if (strcmp(s, "EQUAL_EQUAL") == 0) { + return YP_TOKEN_EQUAL_EQUAL; + } + if (strcmp(s, "EQUAL_EQUAL_EQUAL") == 0) { + return YP_TOKEN_EQUAL_EQUAL_EQUAL; + } + if (strcmp(s, "EQUAL_GREATER") == 0) { + return YP_TOKEN_EQUAL_GREATER; + } + if (strcmp(s, "EQUAL_TILDE") == 0) { + return YP_TOKEN_EQUAL_TILDE; + } + if (strcmp(s, "FLOAT") == 0) { + return YP_TOKEN_FLOAT; + } + if (strcmp(s, "GREATER") == 0) { + return YP_TOKEN_GREATER; + } + if (strcmp(s, "GREATER_EQUAL") == 0) { + return YP_TOKEN_GREATER_EQUAL; + } + if (strcmp(s, "GREATER_GREATER") == 0) { + return YP_TOKEN_GREATER_GREATER; + } + if (strcmp(s, "GREATER_GREATER_EQUAL") == 0) { + return YP_TOKEN_GREATER_GREATER_EQUAL; + } + if (strcmp(s, "GLOBAL_VARIABLE") == 0) { + return YP_TOKEN_GLOBAL_VARIABLE; + } + if (strcmp(s, "IDENTIFIER") == 0) { + return YP_TOKEN_IDENTIFIER; + } + if (strcmp(s, "IMAGINARY_NUMBER") == 0) { + return YP_TOKEN_IMAGINARY_NUMBER; + } + if (strcmp(s, "INSTANCE_VARIABLE") == 0) { + return YP_TOKEN_INSTANCE_VARIABLE; + } + if (strcmp(s, "INTEGER") == 0) { + return YP_TOKEN_INTEGER; + } + if (strcmp(s, "KEYWORD___ENCODING__") == 0) { + return YP_TOKEN_KEYWORD___ENCODING__; + } + if (strcmp(s, "KEYWORD___LINE__") == 0) { + return YP_TOKEN_KEYWORD___LINE__; + } + if (strcmp(s, "KEYWORD___FILE__") == 0) { + return YP_TOKEN_KEYWORD___FILE__; + } + if (strcmp(s, "KEYWORD_ALIAS") == 0) { + return YP_TOKEN_KEYWORD_ALIAS; + } + if (strcmp(s, "KEYWORD_AND") == 0) { + return YP_TOKEN_KEYWORD_AND; + } + if (strcmp(s, "KEYWORD_BEGIN") == 0) { + return YP_TOKEN_KEYWORD_BEGIN; + } + if (strcmp(s, "KEYWORD_BEGIN_UPCASE") == 0) { + return YP_TOKEN_KEYWORD_BEGIN_UPCASE; + } + if (strcmp(s, "KEYWORD_BREAK") == 0) { + return YP_TOKEN_KEYWORD_BREAK; + } + if (strcmp(s, "KEYWORD_CASE") == 0) { + return YP_TOKEN_KEYWORD_CASE; + } + if (strcmp(s, "KEYWORD_CLASS") == 0) { + return YP_TOKEN_KEYWORD_CLASS; + } + if (strcmp(s, "KEYWORD_DEF") == 0) { + return YP_TOKEN_KEYWORD_DEF; + } + if (strcmp(s, "KEYWORD_DEFINED") == 0) { + return YP_TOKEN_KEYWORD_DEFINED; + } + if (strcmp(s, "KEYWORD_DO") == 0) { + return YP_TOKEN_KEYWORD_DO; + } + if (strcmp(s, "KEYWORD_ELSE") == 0) { + return YP_TOKEN_KEYWORD_ELSE; + } + if (strcmp(s, "KEYWORD_ELSIF") == 0) { + return YP_TOKEN_KEYWORD_ELSIF; + } + if (strcmp(s, "KEYWORD_END") == 0) { + return YP_TOKEN_KEYWORD_END; + } + if (strcmp(s, "KEYWORD_END_UPCASE") == 0) { + return YP_TOKEN_KEYWORD_END_UPCASE; + } + if (strcmp(s, "KEYWORD_ENSURE") == 0) { + return YP_TOKEN_KEYWORD_ENSURE; + } + if (strcmp(s, "KEYWORD_FALSE") == 0) { + return YP_TOKEN_KEYWORD_FALSE; + } + if (strcmp(s, "KEYWORD_FOR") == 0) { + return YP_TOKEN_KEYWORD_FOR; + } + if (strcmp(s, "KEYWORD_IF") == 0) { + return YP_TOKEN_KEYWORD_IF; + } + if (strcmp(s, "KEYWORD_IN") == 0) { + return YP_TOKEN_KEYWORD_IN; + } + if (strcmp(s, "KEYWORD_MODULE") == 0) { + return YP_TOKEN_KEYWORD_MODULE; + } + if (strcmp(s, "KEYWORD_NEXT") == 0) { + return YP_TOKEN_KEYWORD_NEXT; + } + if (strcmp(s, "KEYWORD_NIL") == 0) { + return YP_TOKEN_KEYWORD_NIL; + } + if (strcmp(s, "KEYWORD_NOT") == 0) { + return YP_TOKEN_KEYWORD_NOT; + } + if (strcmp(s, "KEYWORD_OR") == 0) { + return YP_TOKEN_KEYWORD_OR; + } + if (strcmp(s, "KEYWORD_REDO") == 0) { + return YP_TOKEN_KEYWORD_REDO; + } + if (strcmp(s, "KEYWORD_RESCUE") == 0) { + return YP_TOKEN_KEYWORD_RESCUE; + } + if (strcmp(s, "KEYWORD_RETRY") == 0) { + return YP_TOKEN_KEYWORD_RETRY; + } + if (strcmp(s, "KEYWORD_RETURN") == 0) { + return YP_TOKEN_KEYWORD_RETURN; + } + if (strcmp(s, "KEYWORD_SELF") == 0) { + return YP_TOKEN_KEYWORD_SELF; + } + if (strcmp(s, "KEYWORD_SUPER") == 0) { + return YP_TOKEN_KEYWORD_SUPER; + } + if (strcmp(s, "KEYWORD_THEN") == 0) { + return YP_TOKEN_KEYWORD_THEN; + } + if (strcmp(s, "KEYWORD_TRUE") == 0) { + return YP_TOKEN_KEYWORD_TRUE; + } + if (strcmp(s, "KEYWORD_UNDEF") == 0) { + return YP_TOKEN_KEYWORD_UNDEF; + } + if (strcmp(s, "KEYWORD_UNLESS") == 0) { + return YP_TOKEN_KEYWORD_UNLESS; + } + if (strcmp(s, "KEYWORD_UNTIL") == 0) { + return YP_TOKEN_KEYWORD_UNTIL; + } + if (strcmp(s, "KEYWORD_WHEN") == 0) { + return YP_TOKEN_KEYWORD_WHEN; + } + if (strcmp(s, "KEYWORD_WHILE") == 0) { + return YP_TOKEN_KEYWORD_WHILE; + } + if (strcmp(s, "KEYWORD_YIELD") == 0) { + return YP_TOKEN_KEYWORD_YIELD; + } + if (strcmp(s, "LABEL") == 0) { + return YP_TOKEN_LABEL; + } + if (strcmp(s, "LAMBDA_BEGIN") == 0) { + return YP_TOKEN_LAMBDA_BEGIN; + } + if (strcmp(s, "LESS") == 0) { + return YP_TOKEN_LESS; + } + if (strcmp(s, "LESS_EQUAL") == 0) { + return YP_TOKEN_LESS_EQUAL; + } + if (strcmp(s, "LESS_EQUAL_GREATER") == 0) { + return YP_TOKEN_LESS_EQUAL_GREATER; + } + if (strcmp(s, "LESS_LESS") == 0) { + return YP_TOKEN_LESS_LESS; + } + if (strcmp(s, "LESS_LESS_EQUAL") == 0) { + return YP_TOKEN_LESS_LESS_EQUAL; + } + if (strcmp(s, "MINUS") == 0) { + return YP_TOKEN_MINUS; + } + if (strcmp(s, "MINUS_AT") == 0) { + return YP_TOKEN_MINUS_AT; + } + if (strcmp(s, "MINUS_EQUAL") == 0) { + return YP_TOKEN_MINUS_EQUAL; + } + if (strcmp(s, "MINUS_GREATER") == 0) { + return YP_TOKEN_MINUS_GREATER; + } + if (strcmp(s, "NEWLINE") == 0) { + return YP_TOKEN_NEWLINE; + } + if (strcmp(s, "NTH_REFERENCE") == 0) { + return YP_TOKEN_NTH_REFERENCE; + } + if (strcmp(s, "PARENTHESIS_LEFT") == 0) { + return YP_TOKEN_PARENTHESIS_LEFT; + } + if (strcmp(s, "PARENTHESIS_RIGHT") == 0) { + return YP_TOKEN_PARENTHESIS_RIGHT; + } + if (strcmp(s, "PERCENT") == 0) { + return YP_TOKEN_PERCENT; + } + if (strcmp(s, "PERCENT_EQUAL") == 0) { + return YP_TOKEN_PERCENT_EQUAL; + } + if (strcmp(s, "PERCENT_LOWER_I") == 0) { + return YP_TOKEN_PERCENT_LOWER_I; + } + if (strcmp(s, "PERCENT_LOWER_W") == 0) { + return YP_TOKEN_PERCENT_LOWER_W; + } + if (strcmp(s, "PERCENT_LOWER_X") == 0) { + return YP_TOKEN_PERCENT_LOWER_X; + } + if (strcmp(s, "PERCENT_UPPER_I") == 0) { + return YP_TOKEN_PERCENT_UPPER_I; + } + if (strcmp(s, "PERCENT_UPPER_W") == 0) { + return YP_TOKEN_PERCENT_UPPER_W; + } + if (strcmp(s, "PIPE") == 0) { + return YP_TOKEN_PIPE; + } + if (strcmp(s, "PIPE_EQUAL") == 0) { + return YP_TOKEN_PIPE_EQUAL; + } + if (strcmp(s, "PIPE_PIPE") == 0) { + return YP_TOKEN_PIPE_PIPE; + } + if (strcmp(s, "PIPE_PIPE_EQUAL") == 0) { + return YP_TOKEN_PIPE_PIPE_EQUAL; + } + if (strcmp(s, "PLUS") == 0) { + return YP_TOKEN_PLUS; + } + if (strcmp(s, "PLUS_AT") == 0) { + return YP_TOKEN_PLUS_AT; + } + if (strcmp(s, "PLUS_EQUAL") == 0) { + return YP_TOKEN_PLUS_EQUAL; + } + if (strcmp(s, "QUESTION_MARK") == 0) { + return YP_TOKEN_QUESTION_MARK; + } + if (strcmp(s, "RATIONAL_NUMBER") == 0) { + return YP_TOKEN_RATIONAL_NUMBER; + } + if (strcmp(s, "REGEXP_BEGIN") == 0) { + return YP_TOKEN_REGEXP_BEGIN; + } + if (strcmp(s, "REGEXP_END") == 0) { + return YP_TOKEN_REGEXP_END; + } + if (strcmp(s, "SEMICOLON") == 0) { + return YP_TOKEN_SEMICOLON; + } + if (strcmp(s, "SLASH") == 0) { + return YP_TOKEN_SLASH; + } + if (strcmp(s, "SLASH_EQUAL") == 0) { + return YP_TOKEN_SLASH_EQUAL; + } + if (strcmp(s, "STAR") == 0) { + return YP_TOKEN_STAR; + } + if (strcmp(s, "STAR_EQUAL") == 0) { + return YP_TOKEN_STAR_EQUAL; + } + if (strcmp(s, "STAR_STAR") == 0) { + return YP_TOKEN_STAR_STAR; + } + if (strcmp(s, "STAR_STAR_EQUAL") == 0) { + return YP_TOKEN_STAR_STAR_EQUAL; + } + if (strcmp(s, "STRING_BEGIN") == 0) { + return YP_TOKEN_STRING_BEGIN; + } + if (strcmp(s, "STRING_CONTENT") == 0) { + return YP_TOKEN_STRING_CONTENT; + } + if (strcmp(s, "STRING_END") == 0) { + return YP_TOKEN_STRING_END; + } + if (strcmp(s, "SYMBOL_BEGIN") == 0) { + return YP_TOKEN_SYMBOL_BEGIN; + } + if (strcmp(s, "TILDE") == 0) { + return YP_TOKEN_TILDE; + } + if (strcmp(s, "TILDE_AT") == 0) { + return YP_TOKEN_TILDE_AT; + } + if (strcmp(s, "WORDS_SEP") == 0) { + return YP_TOKEN_WORDS_SEP; + } + if (strcmp(s, "MAXIMUM") == 0) { + return YP_TOKEN_MAXIMUM; + } + + // Fallback + return YP_TOKEN_INVALID; +} diff --git a/ext/yarp/token_type.h b/ext/yarp/token_type.h index c9cdb3fecf..75e15fe54c 100644 --- a/ext/yarp/token_type.h +++ b/ext/yarp/token_type.h @@ -144,5 +144,7 @@ typedef enum yp_token_type { const char * token_type_to_str(yp_token_type_t token_type); +yp_token_type_t +token_type_from_str(const char *s); #endif // YARP_TOKEN_TYPE_H diff --git a/test-native/LSan.supp b/test-native/LSan.supp new file mode 100644 index 0000000000..5c99fddfa4 --- /dev/null +++ b/test-native/LSan.supp @@ -0,0 +1,2 @@ +# Known issue: https://github.com/google/sanitizers/issues/1501 +leak:realizeClassWithoutSwift diff --git a/test-native/cases/lexer/float b/test-native/cases/lexer/float new file mode 100644 index 0000000000..c0988ce9d1 --- /dev/null +++ b/test-native/cases/lexer/float @@ -0,0 +1,4 @@ +INPUT: +42.56 +TOKEN: +FLOAT diff --git a/test-native/cases/lexer/integer b/test-native/cases/lexer/integer new file mode 100644 index 0000000000..4fece7564e --- /dev/null +++ b/test-native/cases/lexer/integer @@ -0,0 +1,4 @@ +INPUT: +42 +TOKEN: +INTEGER diff --git a/test-native/file.h b/test-native/file.h new file mode 100644 index 0000000000..dd10355d10 --- /dev/null +++ b/test-native/file.h @@ -0,0 +1,36 @@ +#ifndef YARP_TEST_FILE_H +#define YARP_TEST_FILE_H + +#include +#include + +typedef struct { + const char *filepath; + char *contents; + size_t length; +} file_t; + +file_t +read_file(const char *filepath) { + FILE *f = fopen(filepath, "rb"); + if (f == NULL) { + fprintf(stderr, "Unable to read %s\n", filepath); + } + fseek(f, 0, SEEK_END); + size_t length = (size_t) ftell(f); + fseek(f, 0, SEEK_SET); + + char *contents = (char *) malloc(length + 1); + fread(contents, 1, length, f); + fclose(f); + contents[length] = 0; + + return (file_t) { .filepath = filepath, .contents = contents, .length = length }; +} + +void +free_file(file_t f) { + free(f.contents); +} + +#endif // YARP_TEST_FILE_H diff --git a/test-native/formatter.h b/test-native/formatter.h new file mode 100644 index 0000000000..727844f189 --- /dev/null +++ b/test-native/formatter.h @@ -0,0 +1,16 @@ +#ifndef YARP_TEST_FORMATTER_H +#define YARP_TEST_FORMATTER_H + +#define eprintf(...) fprintf(stderr, __VA_ARGS__) + +#define red(...) \ + eprintf("\033[0;31m"); \ + eprintf(__VA_ARGS__); \ + eprintf("\033[0m"); + +#define green(...) \ + eprintf("\033[0;32m"); \ + eprintf(__VA_ARGS__); \ + eprintf("\033[0m"); + +#endif // YARP_TEST_FORMATTER_H diff --git a/test-native/run-all.sh b/test-native/run-all.sh new file mode 100755 index 0000000000..bc7bfcd4a9 --- /dev/null +++ b/test-native/run-all.sh @@ -0,0 +1,23 @@ +#!/usr/bin/env sh + +echo "Running lexer tests..." + +exitcode=0 + +for f in $(find test-native/cases/lexer -type f); do + ./test-native/run-one --lexer "$f" > /dev/null + if [ $? -ne 0 ] + then + exitcode=1 + fi +done + +for f in $(find test-native/cases/parser -type f); do + ./test-native/run-one --parser "$f" > /dev/null + if [ $? -ne 0 ] + then + exitcode=1 + fi +done + +exit $exitcode diff --git a/test-native/run-lexer.c b/test-native/run-lexer.c new file mode 100644 index 0000000000..9f6dcd16da --- /dev/null +++ b/test-native/run-lexer.c @@ -0,0 +1,140 @@ +#include "../ext/yarp/token_type.h" +#include "../ext/yarp/yarp.h" +#include "file.h" +#include "formatter.h" +#include + +typedef struct { + const char *filepath; + const file_t *file; + + char *input; + + char *token; + yp_token_type_t token_type; +} yp_lexer_fixture_t; + +typedef enum { + YP_LEXER_FIXTURE_INITIAL, + YP_LEXER_FIXTURE_READING_INPUT, + YP_LEXER_FIXTURE_READING_TOKEN, +} yp_lexer_fixture_state_t; + +yp_lexer_fixture_t +parse_lexer_fixture(const char *filepath, const file_t *file) { + yp_lexer_fixture_state_t state = YP_LEXER_FIXTURE_INITIAL; + yp_lexer_fixture_t fixture = (yp_lexer_fixture_t) { + .filepath = filepath, + .file = file, + .input = NULL, + .token = NULL, + .token_type = YP_TOKEN_INVALID, + }; + + char *ptr = file->contents; + char *start; + + while (ptr < file->contents + file->length && *ptr) { + switch (state) { + case YP_LEXER_FIXTURE_INITIAL: { + if (strncmp(ptr, "INPUT:\n", 7) == 0) { + ptr += 7; + + start = ptr; + state = YP_LEXER_FIXTURE_READING_INPUT; + } else { + red("Fixture parse error \"%s\":\n" + "Expected INPUT on line 0", + filepath); + exit(1); + } + break; + } + case YP_LEXER_FIXTURE_READING_INPUT: { + if (strncmp(ptr, "TOKEN:\n", 7) == 0) { + fixture.input = (char *) malloc(ptr - start); + strncpy(fixture.input, start, ptr - start - 1); + fixture.input[ptr - start - 1] = 0; + + ptr += 7; + + state = YP_LEXER_FIXTURE_READING_TOKEN; + start = ptr; + } else { + ptr++; + } + break; + } + + case YP_LEXER_FIXTURE_READING_TOKEN: { + if (*ptr == '\n') { + fixture.token = (char *) malloc(ptr - start + 1); + strncpy(fixture.token, start, ptr - start); + fixture.token[ptr - start] = 0; + fixture.token_type = token_type_from_str(fixture.token); + + return fixture; + } else { + ptr++; + } + break; + } + } + } + + return fixture; +} + +void +free_lexer_fixture(yp_lexer_fixture_t *fixture) { + free(fixture->input); + free(fixture->token); +} + +int +run_lexer_fixture(yp_lexer_fixture_t *fixture) { + printf("Input: '%s'\n", fixture->input); + printf("Expected token: '%s'\n", fixture->token); + + if (fixture->token_type == YP_TOKEN_INVALID) { + red("Fixture parse error \"%s\":\n" + "Unrecognized expected token name\n", + fixture->filepath); + return 1; + } + + yp_parser_t parser; + yp_parser_init(&parser, fixture->input, strlen(fixture->input)); + yp_lex_token(&parser); + + yp_token_t actual = parser.current; + + if (actual.type != fixture->token_type) { + red("Error:\n" + "Expected token: %s\n" + "Actual token: %s (value = ", + fixture->token, token_type_to_str(actual.type)); + const char *ptr = actual.start; + while (ptr != actual.end) { + red("%c", *ptr); + ptr++; + } + red(", length = %lu)\n", actual.end - actual.start); + return 1; + } + + return 0; +} + +int +run_lexer(const char *filepath) { + file_t file = read_file(filepath); + + yp_lexer_fixture_t fixture = parse_lexer_fixture(filepath, &file); + int success = run_lexer_fixture(&fixture); + + free_lexer_fixture(&fixture); + free_file(file); + + return success; +} diff --git a/test-native/run-one.c b/test-native/run-one.c new file mode 100644 index 0000000000..0733c0b1fe --- /dev/null +++ b/test-native/run-one.c @@ -0,0 +1,37 @@ +#include "../ext/yarp/token_type.c" +#include "../ext/yarp/yarp.c" +#include "file.h" +#include "formatter.h" +#include "run-lexer.c" +#include "run-parser.c" +#include + +int +main(int argc, char **argv) { + if (argc != 3) { + fprintf(stderr, "Usage:\n\n" + "./run-one --lexer path/to/lexer/test\n" + "./run-one --parser path/to/parser/test\n"); + return 1; + } + + file_t f = read_file(argv[2]); + int exitcode = 1; + + if (strcmp(argv[1], "--lexer") == 0) { + exitcode = run_lexer(f.filepath); + } else if (strcmp(argv[1], "--parser") == 0) { + exitcode = run_parser(f.filepath, f.contents, f.length); + } else { + fprintf(stderr, "--lexer or --parser mode must be provided, given: %s\n", argv[1]); + exitcode = 1; + } + + free_file(f); + if (exitcode == 0) { + green("%s ... passed\n", argv[2]); + } else { + red("%s ... failed, re-run with %s %s %s\n", argv[2], argv[0], argv[1], argv[2]); + } + return exitcode; +} diff --git a/test-native/run-parser.c b/test-native/run-parser.c new file mode 100644 index 0000000000..8c68d1a65d --- /dev/null +++ b/test-native/run-parser.c @@ -0,0 +1,8 @@ +#include "../ext/yarp/token_type.h" +#include "../ext/yarp/yarp.h" + +int +run_parser(const char *filepath, const char *contents, size_t length) { + printf("Running in parser mode: %s\n", filepath); + return 0; +} diff --git a/test/fixtures/lex.rb b/test/fixtures/lex.rb index a1b0b38df0..50f45ba334 100644 --- a/test/fixtures/lex.rb +++ b/test/fixtures/lex.rb @@ -59,6 +59,7 @@ def !@() end 1i 1ri 0 +42.56 0d100 0d100_100 0D100