Commit 33642607 authored by Vivien Kraus's avatar Vivien Kraus
Browse files

Implement the lexer.

parent 96d93573
Pipeline #29488 passed with stage
in 4 minutes and 42 seconds
......@@ -3,7 +3,7 @@
(eval
. (setq company-clang-arguments
(split-string
"-I@abs_top_srcdir@ -I@abs_top_builddir@ -I@abs_top_srcdir@/lib -I@abs_top_builddir@/lib -I@abs_top_srcdir@/src -I@abs_top_builddir@/src @CFLAGS@ @CPPFLAGS@ @OPENMP_CFLAGS@ @ASAN_CFLAGS@"
"-I@abs_top_srcdir@ -I@abs_top_builddir@ -I@abs_top_srcdir@/lib -I@abs_top_builddir@/lib -I@abs_top_srcdir@/src -I@abs_top_builddir@/src -I@abs_top_srcdir@/src/core -I@abs_top_builddir@/src/core -I@abs_top_srcdir@/src/lang -I@abs_top_builddir@/src/lang @CFLAGS@ @CPPFLAGS@ @OPENMP_CFLAGS@ @ASAN_CFLAGS@"
)
)
)
......
......@@ -117,6 +117,7 @@ src/core/check_deal_cpp
src/core/check_do_deal
src/core/check_game_init
src/core/tarot_mainpage.h
src/lang/check_lexer
stamp-h1
tarot-*.tar.gz
test-driver
\ No newline at end of file
......@@ -51,6 +51,7 @@ AC_CONFIG_FILES([
lib/Makefile
src/Makefile
src/core/Makefile
src/lang/Makefile
doc/Makefile
Doxyfile
.dir-locals.el
......
SUBDIRS = core
SUBDIRS = core lang
bin_PROGRAMS =
lib_LTLIBRARIES = libtarot.la
......
bin_PROGRAMS =
noinst_LTLIBRARIES = libtarot-lexer.la
libtarot_lexer_la_SOURCES = \
tarot_lexer.h \
_tarot_lexer_str_to_number.c \
_tarot_lexer_str_to_whitespace.c \
_tarot_lexer_str_to_comment.c \
_tarot_lexer_str_to_str.c \
_tarot_lexer_loc.c \
_tarot_lexer_str_to_token.c
libtarot_lexer_la_CFLAGS = $(OPENMP_CFLAGS) $(ASAN_CFLAGS)
libtarot_lexer_la_LDFLAGS = \
-no-undefined \
-version-info $(LIBTAROT_VERSION_INFO) \
$(LTLIBINTL)
libtarot_lexer_la_LIBADD = $(ASAN_LIBS) $(OPENMP_LIBS)
include_HEADERS =
TESTS = check_lexer
check_PROGRAMS = check_lexer
check_lexer_SOURCES = check_lexer.c
check_lexer_LDADD = libtarot-lexer.la ../core/libtarot-core.la
INDENTED_FILES = \
$(include_HEADERS) \
$(libtarot_lexer_la_SOURCES) \
$(check_lexer_SOURCES)
EXTRA_DIST = indent.stamp
../libtarot.la:
(cd .. && $(MAKE) libtarot.la)
../core/libtarot-core.la:
(cd ../core && $(MAKE) libtarot-core.la)
indent.stamp: $(INDENTED_FILES)
if HAVE_INDENT
(cd $(abs_srcdir) && $(INDENT) -gnu $(INDENTED_FILES)) \
&& echo "Done!" > $(abs_srcdir)/indent.stamp
else
@ >&2 echo "ERROR: you have changed the source code, \
but you don't have indent installed."
exit 1
endif
AM_CPPFLAGS = \
-I $(top_builddir)/lib \
-I$(top_srcdir)/lib \
-I$(top_srcdir)/src \
-I$(top_builddir)/src \
-I$(top_srcdir)/src/core \
-I$(top_builddir)/src/core \
-I$(top_srcdir)/src/lang \
-I$(top_builddir)/src/lang \
-DLOCALEDIR=\"$(localedir)\"
/**
* \file _tarot_lexer_loc.c
*
* Copyright (C) 2017 Vivien Kraus <vivien@planete-kraus.eu>
*
* This program is free software: you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see
* <http://www.gnu.org/licenses/>.
*/
#include <config.h>
#include <stdlib.h>
#include <string.h>
#include "tarot_lexer.h"
void
_tarot_lexer_loc (const char *str, unsigned int n_src, unsigned int *line,
unsigned int *column)
{
unsigned int i = 0;
for (i = 0; i < n_src; ++i)
{
if (str[i] == '\n')
{
++*line;
*column = 0;
}
else
{
++*column;
}
}
}
/**
* \file _tarot_lexer_str_to_comment.c
*
* Copyright (C) 2017 Vivien Kraus <vivien@planete-kraus.eu>
*
* This program is free software: you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see
* <http://www.gnu.org/licenses/>.
*/
#include <config.h>
#include <stdlib.h>
#include <string.h>
#include "tarot_lexer.h"
void
_tarot_lexer_str_to_comment (const char *str, int eof, char **end)
{
(void) eof;
*end = (char *) str;
if (**end == ';')
{
while (**end != '\n' && **end != '\0')
{
++*end;
}
}
}
/**
* \file _tarot_lexer_str_to_number.c
*
* Copyright (C) 2017 Vivien Kraus <vivien@planete-kraus.eu>
*
* This program is free software: you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see
* <http://www.gnu.org/licenses/>.
*/
#include <config.h>
#include <stdlib.h>
#include <string.h>
#include "tarot_lexer.h"
void
_tarot_lexer_str_to_number (const char *str, int eof, unsigned int *value,
char **end)
{
(void) eof;
if (str[0] >= '0' && str[0] <= '9')
{
*value = strtoul (str, end, 10);
}
else
{
*end = (char *) str;
}
}
/**
* \file _tarot_lexer_str_to_str.c
*
* Copyright (C) 2017 Vivien Kraus <vivien@planete-kraus.eu>
*
* This program is free software: you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see
* <http://www.gnu.org/licenses/>.
*/
#include <config.h>
#include <stdlib.h>
#include <string.h>
#include "tarot_lexer.h"
void
_tarot_lexer_str_to_str (const char *str, int eof, const char *searched,
char **end)
{
unsigned int n_str = strlen (str), n_searched = strlen (searched);
if (n_str < n_searched && !eof && strncmp (str, searched, n_str) == 0)
{
*end = (char *) str + n_str;
}
else if (n_str < n_searched)
{
*end = (char *) str;
}
else if (strncmp (str, searched, n_searched) == 0)
{
*end = (char *) str + n_searched;
}
else
{
*end = (char *) str;
}
}
/**
* \file _tarot_lexer_str_to_token.c
*
* Copyright (C) 2017 Vivien Kraus <vivien@planete-kraus.eu>
*
* This program is free software: you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see
* <http://www.gnu.org/licenses/>.
*/
#include <config.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include "tarot_lexer.h"
static void
recover_error (const char *src, int *token, char **end)
{
*end = (char *) src;
while (**end != '\n')
{
++*end;
}
*token = _TAROT_TOKEN_ERROR;
}
#define CASE(WHAT, what) \
case _TAROT_TOKEN_##WHAT: \
_tarot_lexer_str_to_##what; \
break;
#define CASE_FIXED(WHAT, string) \
CASE(WHAT, str (str, eof, string, &test_end))
#define CASE_KEY(key) \
CASE_FIXED (KEY_##key, #key)
#define CASE_VALUE(WHAT, what) \
CASE_FIXED (VALUE_##WHAT, #what)
static void
check_token (const char *str, int eof, int test, int *token,
unsigned int *value, char **end)
{
char *test_end;
unsigned int test_value;
switch (test)
{
CASE (COMMENT, comment (str, eof, &test_end));
CASE (WHITESPACE, whitespace (str, eof, &test_end));
CASE_FIXED (NEWLINE, "\n");
CASE_FIXED (KV_SEP, ":");
CASE_FIXED (OF, "of");
CASE_FIXED (LIST_SEP, ",");
CASE_KEY (N_PLAYERS);
CASE_KEY (WITH_CALL);
CASE_KEY (PLAYER);
CASE_KEY (DEAL);
CASE_KEY (DEAL_ALL);
CASE_KEY (BID);
CASE_KEY (DECL);
CASE_KEY (CALL);
CASE_KEY (DOG);
CASE_KEY (DISCARD);
CASE_KEY (HANDFUL);
CASE_KEY (CARD);
CASE (VALUE_NUMBER, number (str, eof, &test_value, &test_end));
CASE_VALUE (YES, yes);
CASE_VALUE (NO, no);
CASE_VALUE (PLAYER, P);
CASE_VALUE (PASS, pass);
CASE_VALUE (TAKE, take);
CASE_VALUE (PUSH, push);
CASE_VALUE (STRAIGHT, straight);
CASE_VALUE (DOUBLE, double);
CASE_VALUE (KEEP, keep);
CASE_VALUE (EXCUSE, excuse);
CASE_VALUE (PETIT, petit);
CASE_VALUE (ACE, ace);
CASE_VALUE (JACK, jack);
CASE_VALUE (KNIGHT, knight);
CASE_VALUE (QUEEN, queen);
CASE_VALUE (KING, king);
CASE_VALUE (HEARTS, hearts);
CASE_VALUE (CLUBS, clubs);
CASE_VALUE (DIAMONDS, diamonds);
CASE_VALUE (SPADES, spades);
CASE_VALUE (TRUMPS, trumps);
}
if (test_end > *end)
{
*end = test_end;
*token = test;
*value = test_value;
}
}
#define N_TOKENS 41
void
_tarot_lexer_str_to_token (const char *str, int eof, int *token,
unsigned int *value, char **end)
{
/*
We should call most frequent items first. Let's count: do we
have at least around 78 times this token ?
- ERROR: no.
- COMMENT: no.
- WHITESPACE: yes (after each sep and kv_sep).
- NEWLINE: yes (after each kv).
- KV_SEP: yes (at each kv).
- OF: yes (except for petit and excuse, but discard and handfuls
reepated).
- LIST_SEP: no (except for the server).
- KEY_N_PLAYERS: no.
- KEY_...: no.
- KEY_CARD: yes (72 or 75 times).
- VALUE_NUMBER: yes (except for face cards).
- VALUE_{YES,NO}: no.
- VALUE_PLAYER: no (except for the server).
- VALUE_{PASS, TAKE, PUSH, STRAIGHT, DOUBLE, KEEP, EXCUSE, PETIT,
FACE, SUIT}: no.
So, we have WHITESPACE, NEWLINE, KV_SEP, OF, VALUE_NUMBER,
KEY_CARD that are frequent.
*/
static const int order[N_TOKENS] = {
_TAROT_TOKEN_WHITESPACE,
_TAROT_TOKEN_NEWLINE,
_TAROT_TOKEN_KV_SEP,
_TAROT_TOKEN_OF,
_TAROT_TOKEN_VALUE_NUMBER,
_TAROT_TOKEN_KEY_CARD,
/* Less frequent */
_TAROT_TOKEN_COMMENT,
_TAROT_TOKEN_LIST_SEP,
_TAROT_TOKEN_KEY_N_PLAYERS,
_TAROT_TOKEN_KEY_WITH_CALL,
_TAROT_TOKEN_KEY_PLAYER,
_TAROT_TOKEN_KEY_DEAL,
_TAROT_TOKEN_KEY_DEAL_ALL,
_TAROT_TOKEN_KEY_BID,
_TAROT_TOKEN_KEY_DECL,
_TAROT_TOKEN_KEY_CALL,
_TAROT_TOKEN_KEY_DOG,
_TAROT_TOKEN_KEY_DISCARD,
_TAROT_TOKEN_KEY_HANDFUL,
_TAROT_TOKEN_KEY_CARD,
_TAROT_TOKEN_VALUE_YES,
_TAROT_TOKEN_VALUE_NO,
_TAROT_TOKEN_VALUE_PLAYER,
_TAROT_TOKEN_VALUE_PASS,
_TAROT_TOKEN_VALUE_TAKE,
_TAROT_TOKEN_VALUE_PUSH,
_TAROT_TOKEN_VALUE_STRAIGHT,
_TAROT_TOKEN_VALUE_DOUBLE,
_TAROT_TOKEN_VALUE_KEEP,
_TAROT_TOKEN_VALUE_EXCUSE,
_TAROT_TOKEN_VALUE_PETIT,
_TAROT_TOKEN_VALUE_ACE,
_TAROT_TOKEN_VALUE_JACK,
_TAROT_TOKEN_VALUE_KNIGHT,
_TAROT_TOKEN_VALUE_QUEEN,
_TAROT_TOKEN_VALUE_KING,
_TAROT_TOKEN_VALUE_HEARTS,
_TAROT_TOKEN_VALUE_CLUBS,
_TAROT_TOKEN_VALUE_DIAMONDS,
_TAROT_TOKEN_VALUE_SPADES,
_TAROT_TOKEN_VALUE_TRUMPS
};
unsigned int i = 0;
*end = (char *) str;
for (i = 0; i < N_TOKENS; ++i)
{
check_token (str, eof, order[i], token, value, end);
}
if (*end != '\0' && *end == (char *) str)
{
/* Sadly, this is an error */
/* Immediately recover the error */
recover_error (str, token, end);
}
}
/**
* \file _tarot_lexer_str_to_whitespace.c
*
* Copyright (C) 2017 Vivien Kraus <vivien@planete-kraus.eu>
*
* This program is free software: you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see
* <http://www.gnu.org/licenses/>.
*/
#include <config.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include "tarot_lexer.h"
#define WHITESPACE " \t"
#define IS_WHITESPACE(c) (strchr (WHITESPACE, c) != NULL)
void
_tarot_lexer_str_to_whitespace (const char *str, int eof, char **end)
{
(void) eof;
*end = (char *) str;
while (**end != '\0' && IS_WHITESPACE (**end))
{
++*end;
}
}
/**
* \file check_lexer.c
*
* Copyright (C) 2017 Vivien Kraus <vivien@planete-kraus.eu>
*
* This program is free software: you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see
* <http://www.gnu.org/licenses/>.
*/
#include <config.h>
#include <assert.h>
#include <string.h>
#include <stdio.h>
#include "tarot_lexer.h"
#include <tarot.h>
#include "gettext.h"
static void
assert_number_ok (const char *str, int eof, unsigned int exp_value,
const char *remaining)
{
char *end = NULL;
unsigned int value = 0;
_tarot_lexer_str_to_number (str, eof, &value, &end);
assert (end > str && (eof || *end != '\0') && value == exp_value
&& strcmp (end, remaining) == 0);
}
static void
assert_number_waiting (const char *str, int eof)
{
char *end = NULL;
unsigned int value = 0;
_tarot_lexer_str_to_number (str, eof, &value, &end);
assert (*end == '\0');
}
static void
assert_number_not_ok (const char *str, int eof)
{
char *end = NULL;
unsigned int value = 0;
_tarot_lexer_str_to_number (str, eof, &value, &end);
assert ((eof || *end != '\0') && end == (char *) str);
}
static void
assert_whitespace_ok (const char *str, int eof, const char *remaining)
{
char *end = NULL;
_tarot_lexer_str_to_whitespace (str, eof, &end);
assert (end > str && (eof || *end != '\0') && strcmp (end, remaining) == 0);
}
static void
assert_whitespace_waiting (const char *str, int eof)
{
char *end = NULL;
_tarot_lexer_str_to_whitespace (str, eof, &end);
assert (*end == '\0');
}
static void
assert_whitespace_not_ok (const char *str, int eof)
{
char *end = NULL;
_tarot_lexer_str_to_whitespace (str, eof, &end);
assert (end == (char *) str);
}
static void
assert_comment_ok (const char *str, int eof, const char *remaining)
{
char *end = NULL;
_tarot_lexer_str_to_comment (str, eof, &end);
assert (end > str && (eof || *end != '\0') && strcmp (end, remaining) == 0);
}
static void
assert_comment_waiting (const char *str, int eof)
{
char *end = NULL;
_tarot_lexer_str_to_comment (str, eof, &end);
assert (*end == '\0');
}
static void
assert_comment_not_ok (const char *str, int eof)
{
char *end = NULL;
_tarot_lexer_str_to_comment (str, eof, &end);
assert (end == (char *) str);
}
static void
assert_fixed_ok (const char *str, int eof, const char *searched,
const char *remaining)
{
char *end = NULL;
_tarot_lexer_str_to_str (str, eof, searched, &end);
assert (end > str && strncmp (str, searched, strlen (searched)) == 0
&& strcmp (end, remaining) == 0);
}
static void
assert_fixed_waiting (const char *str, int eof, const char *searched)
{
cha