forked from github/server
Pretty good progress on the OrderParser implementaiton.
Still finding bugs by unit-testing, though.
This commit is contained in:
parent
0c4d1ec2b2
commit
b1cd9bcfef
|
@ -36,6 +36,7 @@ Thumbs.db
|
||||||
*.cfg
|
*.cfg
|
||||||
*.cmd
|
*.cmd
|
||||||
tmp/
|
tmp/
|
||||||
|
tests/orders.txt
|
||||||
tests/config.lua
|
tests/config.lua
|
||||||
tests/reports/
|
tests/reports/
|
||||||
tests/data/185.dat
|
tests/data/185.dat
|
||||||
|
|
|
@ -81,6 +81,7 @@ int RunAllTests(int argc, char *argv[])
|
||||||
ADD_SUITE(functions);
|
ADD_SUITE(functions);
|
||||||
ADD_SUITE(gamedata);
|
ADD_SUITE(gamedata);
|
||||||
ADD_SUITE(language);
|
ADD_SUITE(language);
|
||||||
|
ADD_SUITE(order_parser);
|
||||||
ADD_SUITE(parser);
|
ADD_SUITE(parser);
|
||||||
ADD_SUITE(password);
|
ADD_SUITE(password);
|
||||||
ADD_SUITE(umlaut);
|
ADD_SUITE(umlaut);
|
||||||
|
|
|
@ -16,7 +16,7 @@ language.test.c
|
||||||
# log.test.c
|
# log.test.c
|
||||||
message.test.c
|
message.test.c
|
||||||
# nrmessage.test.c
|
# nrmessage.test.c
|
||||||
# order_parser.test.c
|
order_parser.test.c
|
||||||
# param.test.c
|
# param.test.c
|
||||||
parser.test.c
|
parser.test.c
|
||||||
password.test.c
|
password.test.c
|
||||||
|
|
|
@ -4,7 +4,10 @@
|
||||||
|
|
||||||
#include "order_parser.h"
|
#include "order_parser.h"
|
||||||
|
|
||||||
|
#include <assert.h>
|
||||||
|
#include <ctype.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
struct OrderParserStruct {
|
struct OrderParserStruct {
|
||||||
void *m_userData;
|
void *m_userData;
|
||||||
|
@ -18,34 +21,208 @@ struct OrderParserStruct {
|
||||||
int m_lineNumber;
|
int m_lineNumber;
|
||||||
};
|
};
|
||||||
|
|
||||||
void OP_SetUnitHandler(OP_Parser op, OP_UnitHandler handler)
|
void OP_SetUnitHandler(OP_Parser parser, OP_UnitHandler handler)
|
||||||
{
|
{
|
||||||
op->m_unitHandler = handler;
|
parser->m_unitHandler = handler;
|
||||||
}
|
}
|
||||||
|
|
||||||
void OP_SetFactionHandler(OP_Parser op, OP_FactionHandler handler) {
|
void OP_SetFactionHandler(OP_Parser parser, OP_FactionHandler handler) {
|
||||||
op->m_factionHandler = handler;
|
parser->m_factionHandler = handler;
|
||||||
}
|
}
|
||||||
|
|
||||||
void OP_SetOrderHandler(OP_Parser op, OP_OrderHandler handler) {
|
void OP_SetOrderHandler(OP_Parser parser, OP_OrderHandler handler) {
|
||||||
op->m_orderHandler = handler;
|
parser->m_orderHandler = handler;
|
||||||
}
|
}
|
||||||
|
|
||||||
void OP_SetUserData(OP_Parser op, void *userData) {
|
void OP_SetUserData(OP_Parser parser, void *userData) {
|
||||||
op->m_userData = userData;
|
parser->m_userData = userData;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void buffer_free(OP_Parser parser)
|
||||||
|
{
|
||||||
|
/* TODO: recycle buffers, reduce mallocs. */
|
||||||
|
free(parser->m_buffer);
|
||||||
|
parser->m_bufferEnd = parser->m_bufferPtr = parser->m_buffer = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
void OP_ParserReset(OP_Parser parser) {
|
||||||
|
parser->m_lineNumber = 1;
|
||||||
|
buffer_free(parser);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
OP_Parser OP_ParserCreate(void)
|
OP_Parser OP_ParserCreate(void)
|
||||||
{
|
{
|
||||||
OP_Parser parser = calloc(1, sizeof(struct OrderParserStruct));
|
OP_Parser parser = calloc(1, sizeof(struct OrderParserStruct));
|
||||||
|
OP_ParserReset(parser);
|
||||||
return parser;
|
return parser;
|
||||||
}
|
}
|
||||||
|
|
||||||
void OP_ParserFree(OP_Parser op) {
|
void OP_ParserFree(OP_Parser parser) {
|
||||||
free(op);
|
free(parser->m_buffer);
|
||||||
|
free(parser);
|
||||||
}
|
}
|
||||||
|
|
||||||
enum OP_Status OP_Parse(OP_Parser op, const char *s, int len, int isFinal)
|
static enum OP_Error buffer_append(OP_Parser parser, const char *s, int len)
|
||||||
{
|
{
|
||||||
|
if (parser->m_buffer == NULL) {
|
||||||
|
parser->m_buffer = malloc(len + 1);
|
||||||
|
if (!parser->m_buffer) {
|
||||||
|
return OP_ERROR_NO_MEMORY;
|
||||||
|
}
|
||||||
|
memcpy(parser->m_buffer, s, len);
|
||||||
|
parser->m_buffer[len] = '\0';
|
||||||
|
parser->m_bufferPtr = parser->m_buffer;
|
||||||
|
parser->m_bufferEnd = parser->m_buffer + len;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
size_t total = len;
|
||||||
|
char * buffer;
|
||||||
|
total += (parser->m_bufferEnd - parser->m_bufferPtr);
|
||||||
|
/* TODO: recycle buffers, reduce mallocs. */
|
||||||
|
buffer = malloc(total + 1);
|
||||||
|
memcpy(buffer, parser->m_bufferPtr, total - len);
|
||||||
|
memcpy(buffer + total - len, s, len);
|
||||||
|
buffer[total] = '\0';
|
||||||
|
free(parser->m_buffer);
|
||||||
|
parser->m_buffer = buffer;
|
||||||
|
if (!parser->m_buffer) {
|
||||||
|
return OP_ERROR_NO_MEMORY;
|
||||||
|
}
|
||||||
|
parser->m_bufferPtr = parser->m_buffer;
|
||||||
|
parser->m_bufferEnd = parser->m_buffer + total;
|
||||||
|
}
|
||||||
|
return OP_ERROR_NONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static enum OP_Error handle_line(OP_Parser parser) {
|
||||||
|
if (parser->m_orderHandler) {
|
||||||
|
parser->m_orderHandler(parser->m_userData, parser->m_bufferPtr);
|
||||||
|
}
|
||||||
|
return OP_ERROR_NONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static char *skip_spaces(char *pos) {
|
||||||
|
char *next;
|
||||||
|
for (next = pos; *next && *next != '\n'; ++next) {
|
||||||
|
/* TODO: handle unicode whitespace */
|
||||||
|
if (!isspace(*next)) break;
|
||||||
|
}
|
||||||
|
return next;
|
||||||
|
}
|
||||||
|
|
||||||
|
static enum OP_Status parse_buffer(OP_Parser parser, int isFinal)
|
||||||
|
{
|
||||||
|
char * pos = strpbrk(parser->m_bufferPtr, "\\;\n");
|
||||||
|
while (pos) {
|
||||||
|
enum OP_Error code;
|
||||||
|
size_t len = pos - parser->m_bufferPtr;
|
||||||
|
char *next;
|
||||||
|
|
||||||
|
switch (*pos) {
|
||||||
|
case '\n':
|
||||||
|
*pos = '\0';
|
||||||
|
code = handle_line(parser);
|
||||||
|
++parser->m_lineNumber;
|
||||||
|
if (code != OP_ERROR_NONE) {
|
||||||
|
parser->m_errorCode = code;
|
||||||
|
return OP_STATUS_ERROR;
|
||||||
|
}
|
||||||
|
parser->m_bufferPtr = pos + 1;
|
||||||
|
pos = strpbrk(parser->m_bufferPtr, "\\;\n");
|
||||||
|
break;
|
||||||
|
case '\\':
|
||||||
|
/* if this is the last non-space before the line break, then lines need to be joined */
|
||||||
|
next = skip_spaces(pos + 1);
|
||||||
|
if (*next == '\n') {
|
||||||
|
ptrdiff_t shift = (next + 1 - pos);
|
||||||
|
assert(shift > 0);
|
||||||
|
memmove(parser->m_bufferPtr + shift, parser->m_bufferPtr, len);
|
||||||
|
parser->m_bufferPtr += shift;
|
||||||
|
pos = strpbrk(next + 1, "\\;\n");
|
||||||
|
++parser->m_lineNumber;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
/* this is not multi-line input yet, so do nothing */
|
||||||
|
pos = strpbrk(pos + 1, "\\;\n");
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case ';':
|
||||||
|
/* the current line ends in a comment */
|
||||||
|
*pos++ = '\0';
|
||||||
|
handle_line(parser);
|
||||||
|
/* find the end of the comment so we can skip it.
|
||||||
|
* obs: multi-line comments are possible with a backslash. */
|
||||||
|
do {
|
||||||
|
next = strpbrk(pos, "\\\n");
|
||||||
|
if (next) {
|
||||||
|
if (*next == '\n') {
|
||||||
|
/* no more lines in this comment, we're done: */
|
||||||
|
pos = next + 1;
|
||||||
|
++parser->m_lineNumber;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
/* is this backslash the final character? */
|
||||||
|
next = skip_spaces(pos + 1);
|
||||||
|
if (*next == '\n') {
|
||||||
|
/* we have a multi-line comment! */
|
||||||
|
pos = next + 1;
|
||||||
|
++parser->m_lineNumber;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
/* keep looking for a backslash */
|
||||||
|
pos = next;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} while (next && *next);
|
||||||
|
|
||||||
|
if (next && pos < parser->m_bufferEnd) {
|
||||||
|
/* we skip the comment, and there is more data in the buffer */
|
||||||
|
parser->m_bufferPtr = pos;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
/* we exhausted the buffer before we got to the end of the comment */
|
||||||
|
if (isFinal) {
|
||||||
|
/* the input ended on this comment line, which is fine */
|
||||||
return OP_STATUS_OK;
|
return OP_STATUS_OK;
|
||||||
}
|
}
|
||||||
|
else {
|
||||||
|
/* skip what we have of the comment, keep the semicolon, keep going */
|
||||||
|
ptrdiff_t skip = parser->m_bufferEnd - parser->m_bufferPtr;
|
||||||
|
if (skip > 1) {
|
||||||
|
parser->m_bufferPtr += (skip - 1);
|
||||||
|
parser->m_bufferPtr[0] = ';';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
parser->m_errorCode = OP_ERROR_SYNTAX;
|
||||||
|
return OP_STATUS_ERROR;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (isFinal && parser->m_bufferPtr < parser->m_bufferEnd) {
|
||||||
|
/* this line ends without a line break */
|
||||||
|
handle_line(parser);
|
||||||
|
}
|
||||||
|
return OP_STATUS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
enum OP_Status OP_Parse(OP_Parser parser, const char *s, int len, int isFinal)
|
||||||
|
{
|
||||||
|
enum OP_Error code;
|
||||||
|
|
||||||
|
if (parser->m_bufferPtr >= parser->m_bufferEnd) {
|
||||||
|
buffer_free(parser);
|
||||||
|
}
|
||||||
|
|
||||||
|
code = buffer_append(parser, s, len);
|
||||||
|
if (code != OP_ERROR_NONE) {
|
||||||
|
parser->m_errorCode = code;
|
||||||
|
return OP_STATUS_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
return parse_buffer(parser, isFinal);
|
||||||
|
}
|
||||||
|
|
|
@ -34,11 +34,12 @@ typedef void(*OP_UnitHandler) (void *userData, int no);
|
||||||
typedef void(*OP_OrderHandler) (void *userData, const char *str);
|
typedef void(*OP_OrderHandler) (void *userData, const char *str);
|
||||||
|
|
||||||
OP_Parser OP_ParserCreate(void);
|
OP_Parser OP_ParserCreate(void);
|
||||||
void OP_ParserFree(OP_Parser op);
|
void OP_ParserFree(OP_Parser parser);
|
||||||
enum OP_Status OP_Parse(OP_Parser op, const char *s, int len, int isFinal);
|
void OP_ParserReset(OP_Parser parser);
|
||||||
void OP_SetUnitHandler(OP_Parser op, OP_UnitHandler handler);
|
enum OP_Status OP_Parse(OP_Parser parser, const char *s, int len, int isFinal);
|
||||||
void OP_SetFactionHandler(OP_Parser op, OP_FactionHandler handler);
|
void OP_SetUnitHandler(OP_Parser parser, OP_UnitHandler handler);
|
||||||
void OP_SetOrderHandler(OP_Parser op, OP_OrderHandler handler);
|
void OP_SetFactionHandler(OP_Parser parser, OP_FactionHandler handler);
|
||||||
void OP_SetUserData(OP_Parser op, void *userData);
|
void OP_SetOrderHandler(OP_Parser parser, OP_OrderHandler handler);
|
||||||
|
void OP_SetUserData(OP_Parser parser, void *userData);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -0,0 +1,65 @@
|
||||||
|
#ifdef _MSC_VER
|
||||||
|
#include <platform.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "order_parser.h"
|
||||||
|
#include "strings.h"
|
||||||
|
|
||||||
|
#include <CuTest.h>
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
static void test_parse_noop(CuTest *tc) {
|
||||||
|
OP_Parser parser;
|
||||||
|
parser = OP_ParserCreate();
|
||||||
|
CuAssertPtrNotNull(tc, parser);
|
||||||
|
CuAssertIntEquals(tc, OP_STATUS_OK, OP_Parse(parser, "Hello World", 11, 1));
|
||||||
|
OP_ParserFree(parser);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void copy_line(void *udata, const char *str) {
|
||||||
|
char *dst = (char *)udata;
|
||||||
|
if (dst) {
|
||||||
|
strcpy(dst, str);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_parse_orders(CuTest *tc) {
|
||||||
|
OP_Parser parser;
|
||||||
|
char lastline[1024];
|
||||||
|
|
||||||
|
parser = OP_ParserCreate();
|
||||||
|
OP_SetUserData(parser, lastline);
|
||||||
|
OP_SetOrderHandler(parser, copy_line);
|
||||||
|
CuAssertPtrNotNull(tc, parser);
|
||||||
|
CuAssertIntEquals(tc, OP_STATUS_OK, OP_Parse(parser, "Hello World", 11, 1));
|
||||||
|
CuAssertStrEquals(tc, "Hello World", lastline);
|
||||||
|
OP_ParserReset(parser);
|
||||||
|
CuAssertIntEquals(tc, OP_STATUS_OK, OP_Parse(parser, "Hello World\n", 12, 1));
|
||||||
|
CuAssertStrEquals(tc, "Hello World", lastline);
|
||||||
|
OP_ParserReset(parser);
|
||||||
|
CuAssertIntEquals(tc, OP_STATUS_OK, OP_Parse(parser, "Hello\\\n World", 13, 1));
|
||||||
|
CuAssertStrEquals(tc, "Hello World", lastline);
|
||||||
|
OP_ParserReset(parser);
|
||||||
|
CuAssertIntEquals(tc, OP_STATUS_OK, OP_Parse(parser, "Hello;World", 11, 1));
|
||||||
|
CuAssertStrEquals(tc, "Hello", lastline);
|
||||||
|
OP_ParserReset(parser);
|
||||||
|
CuAssertIntEquals(tc, OP_STATUS_OK, OP_Parse(parser, "Hello\\World", 11, 1));
|
||||||
|
CuAssertStrEquals(tc, "Hello\\World", lastline);
|
||||||
|
OP_ParserReset(parser);
|
||||||
|
CuAssertIntEquals(tc, OP_STATUS_OK, OP_Parse(parser, "Hello ", 6, 0));
|
||||||
|
CuAssertIntEquals(tc, OP_STATUS_OK, OP_Parse(parser, "World", 5, 1));
|
||||||
|
CuAssertStrEquals(tc, "Hello World", lastline);
|
||||||
|
OP_ParserReset(parser);
|
||||||
|
CuAssertIntEquals(tc, OP_STATUS_OK, OP_Parse(parser, "Hello\\World \\", 14, 1));
|
||||||
|
CuAssertStrEquals(tc, "Hello\\World ", lastline);
|
||||||
|
OP_ParserFree(parser);
|
||||||
|
}
|
||||||
|
|
||||||
|
CuSuite *get_order_parser_suite(void)
|
||||||
|
{
|
||||||
|
CuSuite *suite = CuSuiteNew();
|
||||||
|
SUITE_ADD_TEST(suite, test_parse_noop);
|
||||||
|
SUITE_ADD_TEST(suite, test_parse_orders);
|
||||||
|
return suite;
|
||||||
|
}
|
Loading…
Reference in New Issue