#include <assert.h>
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <inttypes.h>
#include "idl/processor.h"
#include "scanner.h"
#include "parser.h"
#include "CUnit/Theory.h"
static int
compare_position(idl_position_t *a, idl_position_t *b)
{
if (!a || !b)
return !a && !b;
if (a->line != b->line)
return (int)a->line - (int)b->line;
if (a->column != b->column)
return (int)a->column - (int)b->column;
if (!a->file)
return !b->file ? 0 : -1;
if (!b->file)
return 1;
return strcmp(a->file->name, b->file->name);
}
static void
assert_location(idl_location_t *a, idl_location_t *b)
{
fprintf(stderr, "first.line: %"PRIu32", %"PRIu32"\n", a->first.line, b->first.line);
fprintf(stderr, "first.column: %"PRIu32", %"PRIu32"\n", a->first.column, b->first.column);
CU_ASSERT_EQ (compare_position(&a->first, &b->first), 0);
fprintf(stderr, "last.line: %"PRIu32", %"PRIu32"\n", a->last.line, b->last.line);
fprintf(stderr, "last.column: %"PRIu32", %"PRIu32"\n", a->last.column, b->last.column);
CU_ASSERT_EQ (compare_position(&a->last, &b->last), 0);
}
static void
assert_token(idl_token_t *tok, idl_token_t *xtok)
{
fprintf(stderr, "code: %"PRId32", %"PRId32"\n", tok->code, xtok->code);
CU_ASSERT_EQ (tok->code, xtok->code);
assert_location(&tok->location, &xtok->location);
if (tok->code != xtok->code)
return;
switch (xtok->code) {
case IDL_TOKEN_IDENTIFIER:
case IDL_TOKEN_STRING_LITERAL:
case IDL_TOKEN_PP_NUMBER:
if (!xtok->value.str)
break;
CU_ASSERT_NEQ (tok->value.str, NULL);
if (tok->value.str) {
fprintf(stderr, "string: '%s', '%s'\n", tok->value.str, xtok->value.str);
} else {
fprintf(stderr, "string: null, '%s'\n", xtok->value.str);
break;
}
CU_ASSERT_STREQ (tok->value.str, xtok->value.str);
break;
default:
break;
}
}
static void
test_scanner(idl_pstate_t *pstate, idl_token_t *tokvec)
{
int32_t code;
idl_token_t tok;
for (int i = 0; tokvec[i].code >= 0; i++) {
code = idl_scan(pstate, &tok);
if (code < 0) {
CU_FAIL("unexpected return code");
break;
}
assert_token(&tok, &tokvec[i]);
switch (code) {
case IDL_TOKEN_IDENTIFIER:
case IDL_TOKEN_PP_NUMBER:
case IDL_TOKEN_STRING_LITERAL:
case IDL_TOKEN_LINE_COMMENT:
case IDL_TOKEN_COMMENT:
free(tok.value.str);
break;
default:
break;
}
if (tokvec[i].code == 0)
break;
}
}
static void
test(const char *str, idl_token_t *tokvec)
{
idl_pstate_t *pstate = NULL;
if (idl_create_pstate(0u, NULL, &pstate)) {
CU_FAIL("Failed to create parser");
return;
}
pstate->scanner.cursor = str;
pstate->scanner.limit = str + strlen(str);
pstate->scanner.position.source = NULL;
pstate->scanner.position.file = NULL;
pstate->scanner.position.line = 1;
pstate->scanner.position.column = 1;
test_scanner(pstate, tokvec);
pstate->scanner.cursor = NULL;
pstate->scanner.limit = NULL;
idl_delete_pstate(pstate);
}
#define C(c, fl, fc, ll, lc) \
{ c, { .chr = 0 }, { { NULL, NULL, fl, fc }, { NULL, NULL, ll, lc } } }
#define T(c, fl, fc, ll, lc) \
{ c, { .chr = 0 }, { { NULL, NULL, fl, fc }, { NULL, NULL, ll, lc } } }
#define T_STR(c, s, fl, fc, ll, lc) \
{ c, { .str = s }, { { NULL, NULL, fl, fc }, { NULL, NULL, ll, lc } } }
#define T_ULLNG(c, n, fl, fc, ll, lc) \
{ c, { .ullng = n }, { { NULL, NULL, fl, fc }, { NULL, NULL, ll, lc } } }
#define T_LDBL(c, n, fl, fc, ll, lc) \
{ c, { .ldbl = n }, { { NULL, NULL, fl, fc }, { NULL, NULL, ll, lc } } }
#define T0(fl, fc) T('\0', fl, fc, fl, fc)
#define TLC(fl, fc, ll, lc) T(IDL_TOKEN_LINE_COMMENT, fl, fc, ll, lc)
#define TC(fl, fc, ll, lc) T(IDL_TOKEN_COMMENT, fl, fc, ll, lc)
#define TCL(fl, fc, ll, lc) T(IDL_TOKEN_CHAR_LITERAL, fl, fc, ll, lc)
#define TSL(fl, fc, ll, lc) \
T_STR(IDL_TOKEN_STRING_LITERAL, NULL, fl, fc, ll, lc)
#define TSL_STR(str, fl, fc, ll, lc) \
T_STR(IDL_TOKEN_STRING_LITERAL, str, fl, fc ll, lc)
#define TIL(ullng, fl, fc, ll, lc) \
T_ULLNG(IDL_TOKEN_INTEGER_LITERAL, 0, fl, fc, ll, lc)
#define TFL(ldbl, fl, fc, ll, lc) \
T_LDBL(IDL_TOKEN_FLOATING_PT_LITERAL, 0.0, fl, fc, ll, lc)
#define TI(fl, fc, ll, lc) \
T_STR(IDL_TOKEN_IDENTIFIER, NULL, fl, fc, ll, lc)
#define TI_STR(str, fl, fc, ll, lc) \
T_STR(IDL_TOKEN_IDENTIFIER, str, fl, fc, ll, lc)
#define TN_STR(str, fl, fc, ll, lc) \
T_STR(IDL_TOKEN_PP_NUMBER, str, fl, fc, ll, lc)
#define TS(fl, fc, ll, lc) T(IDL_TOKEN_SCOPE, fl, fc, ll, lc)
#define TOKVEC(...) (idl_token_t[]){ __VA_ARGS__}
CU_Test(idl_scanner, blank)
{ test("", TOKVEC( T0(1,1) )); }
CU_Test(idl_scanner, blank_cseq)
{ test("\\\n", TOKVEC( T0(2,1) )); }
CU_Test(idl_scanner, blank_wsp_cseq_wsp)
{ test(" \\\n ", TOKVEC( T0(2,3) )); }
CU_Test(idl_scanner, blank_2x_cseq)
{ test("\\\n\\\n", TOKVEC( T0(3,1) )); }
CU_Test(idl_scanner, line_comment)
{ test("//", TOKVEC( TLC(1,1,1,3), T0(1,3) )); }
CU_Test(idl_scanner, line_comment_wrp_cseq)
{ test("/\\\n/\\\n", TOKVEC( TLC(1,1,3,1), T0(3,1) )); }
CU_Test(idl_scanner, line_comment_wrp_cseq_ident)
{ test("//\\\nfoo\\\nbar\nbaz", TOKVEC( TLC(1,1,3,4), C('\n',3,4,4,1), TI(4,1,4,4), T0(4,4) )); }
CU_Test(idl_scanner, comment)
{ test("/**/", TOKVEC( TC(1,1,1,5), T0(1,5) )); }
CU_Test(idl_scanner, comment_x)
{ test("/*/*/", TOKVEC( TC(1,1,1,6), T0(1,6) )); }
CU_Test(idl_scanner, comment_wrp_cseq)
{ test("/\\\n*\\\n*\\\n/", TOKVEC( TC(1,1,4,2), T0(4,2) )); }
CU_Test(idl_scanner, comment_wrp_cseq_ident)
{ test("/\\\n*foo\\\n/bar\\\n*\\\n/baz", TOKVEC( TC(1,1,5,2), TI(5,2,5,5), T0(5,5) )); }
CU_Test(idl_scanner, char_literal)
{ test("\'f\'", TOKVEC( TCL(1,1,1,4), T0(1,4) )); }
CU_Test(idl_scanner, char_literal_wrp_cseq)
{ test("\'\\n\\\n\'\\\n", TOKVEC( TCL(1,1,2,2), T0(3,1) )); }
CU_Test(idl_scanner, string_literal)
{ test("\"foo bar baz\"", TOKVEC( TSL(1,1,1,14), T0(1,14) )); }
CU_Test(idl_scanner, string_literal_wrp_seq)
{ test("\"foo bar baz\\\n\"\\\n", TOKVEC( TSL(1,1,2,2), T0(3,1) )); }
CU_Test(idl_scanner, integer_literal)
{ test("1", TOKVEC( TIL(1,1,1,1,2), T0(1,2) )); }
CU_Test(idl_scanner, integer_literal_hex_dot)
{ test("0x1.", TOKVEC( TIL(0x1,1,1,1,4), T('.',1,4,1,5), T0(1,5) )); }
CU_Test(idl_scanner, floating_pt_literal)
{ test("1\\\n23.", TOKVEC( TFL(123.0,1,1,2,4), T0(2,4) )); }
CU_Test(idl_scanner, floating_pt_literal_dot_fraction)
{ test(".1", TOKVEC( TFL(.1,1,1,1,3), T0(1,3) )); }
CU_Test(idl_scanner, floating_pt_literal_int_dot_fraction_exp)
{ test("1.1e+3", TOKVEC( TFL(1.1e3,1,1,1,7), T0(1,7) )); }
CU_Test(idl_scanner, floating_pt_literal_int_dot_fraction)
{ test("1.1e-3e3", TOKVEC( TFL(1.1e-3,1,1,1,7), TI_STR("e3", 1,7,1,9), T0(1,9) )); }
CU_Test(idl_scanner, ident)
{ test("a", TOKVEC( TI(1,1,1,2), T0(1,2) )); }
CU_Test(idl_scanner, ident_tr_cseq)
{ test("a\\\r\n", TOKVEC( TI(1,1,1,2), T0(2,1) )); }
CU_Test(idl_scanner, ident_wrp_cseq)
{ test("\\\na\\\r\nb\\\n", TOKVEC( TI(2,1,3,2), T0(4,1) )); }
CU_Test(idl_scanner, scope)
{ test("::", TOKVEC( TS(1,1,1,3), T0(1,3) )); }
CU_Test(idl_scanner, scope_wrp_cseq)
{ test("\\\n:\\\r\n:\\\n", TOKVEC( TS(2,1,3,2), T0(4,1) )); }
CU_Test(idl_scanner, scope_l)
{ test("foo::", TOKVEC( TI(1,1,1,4), TS(1,4,1,6), T0(1,6) )); }
CU_Test(idl_scanner, scope_l_wrp_cseq)
{ test("foo\\\n::", TOKVEC( TI(1,1,1,4), TS(2,1,2,3), T0(2,3) )); }
CU_Test(idl_scanner, scope_non_l)
{ test("foo ::", TOKVEC( TI(1,1,1,4), TS(1,5,1,7), T0(1,7) )); }
CU_Test(idl_scanner, scope_r)
{ test("::foo", TOKVEC( TS(1,1,1,3), TI(1,3,1,6), T0(1,6) )); }
CU_Test(idl_scanner, scope_r_wrp_cseq)
{ test("::\\\r\nfoo", TOKVEC( TS(1,1,1,3), TI(2,1,2,4), T0(2,4) )); }
CU_Test(idl_scanner, scope_non_r)
{ test(":: foo", TOKVEC( TS(1,1,1,3), TI(1,4,1,7), T0(1,7) )); }
CU_Test(idl_scanner, scope_lr)
{ test("foo::bar",
TOKVEC( TI(1,1,1,4), TS(1,4,1,6), TI(1,6,1,9), T0(1,9) )); }
CU_Test(idl_scanner, scope_lr_wrp_cseq)
{ test("foo\\\n::\\\r\nbar",
TOKVEC( TI(1,1,1,4), TS(2,1,2,3), TI(3,1,3,4), T0(3,4) )); }
CU_Test(idl_scanner, scope_non_lr)
{ test("foo :: bar",
TOKVEC( TI(1,1,1,4), TS(1,5,1,7), TI(1,8,1,11), T0(1,11) )); }
CU_Test(idl_scanner, scope_non_lr_l)
{ test("foo:: bar",
TOKVEC( TI(1,1,1,4), TS(1,4,1,6), TI(1,7,1,10), T0(1,10) )); }
CU_Test(idl_scanner, scope_non_lr_r)
{ test("foo ::bar",
TOKVEC( TI(1,1,1,4), TS(1,5,1,7), TI(1,7,1,10), T0(1,10) )); }
CU_Test(idl_scanner, hash_line)
{ test("#line\\\n 1",
TOKVEC( T('#',1,1,1,2), TI_STR("line",1,2,1,6), TN_STR("1",2,2,2,3), T0(2,3) )); }
CU_Test(idl_scanner, hash_pragma)
{ test("#pragma keylist foo bar baz\n",
TOKVEC( T('#',1,1,1,2),
TI_STR("pragma",1,2,1,8),
TI_STR("keylist",1,9,1,16),
TI_STR("foo",1,17,1,20),
TI_STR("bar",1,21,1,24),
TI_STR("baz",1,25,1,28),
T('\n',1,28,2,1),
T0(2,1) ));
}