Skip to content

Commit dcd7a61

Browse files
committed
peg.h разделен на peg.h и peg-internal.h. Первый файл содержит API, которое нужно для
использования парсера, второй -- API, которым пользуется сгенерированный парсер. Также обновлен Readme в части доступного API парсера.
1 parent f8209b4 commit dcd7a61

File tree

4 files changed

+340
-287
lines changed

4 files changed

+340
-287
lines changed

README.md

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,21 @@ pegjs-c-plugin
44
Плагин для библиотеки [PEGjs](http://pegjs.org/), позволяющий сгенерировать исходный код на
55
C89 (также известном, как ANSI C).
66

7+
API парсера
8+
-----------
9+
На вход подается диапазон -- массив байт для разбора. На выходе -- древовидная структура `Result`.
10+
После окончания работы с данной структурой, надо освободить выделенную под нее память, вызвав
11+
`freeResult`.
12+
13+
Структура `Result` содержит следующую информацию:
14+
15+
* Регион разбираемых данных, для которого была сформирована данная структура, в том числе указатель
16+
на начало и конец данных, а также информацию о строке и стобце для обоих концов (еще не реализовано).
17+
* Количество дочерных элементов. Все дочерние элементы охватывают регионы, которые лежат внутри
18+
родительского региона.
19+
* Указатель на пользовательские данные. Действия могут прикреплять туда свои данные во время разбора.
20+
Парсер не следит за их содержимым.
21+
722
Ограничения
823
-----------
924
В отличие от оригинала, в Си нет автоматического управления памятью, и строгая типизация,

peg-internal.h

Lines changed: 272 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,272 @@
1+
/** @file Содержит определения внутренних типов и функций, используемых парсером.
2+
Данный файл долен быть С89-совместимым, так, чтобы `gcc -pedantic -Wall -Wextra -ansi -std=c89`
3+
не выдавал предупреждений.
4+
*/
5+
6+
#include "peg.h"
7+
8+
/* Для memcmp, memcpy. */
9+
#include <string.h>
10+
/* Для malloc/calloc/free/bsearch. */
11+
#include <stdlib.h>
12+
/* Для va_*, используемых в функции wrap. */
13+
#include <stdarg.h>
14+
#include <assert.h>
15+
16+
struct Literal {
17+
/** Длина литерала (массива data). */
18+
unsigned int len;
19+
/** Массив символов литерала. */
20+
const char* data;
21+
/** Сообщение об ожидаемом элементе, если сопоставление с литералом пройдет неудачно. */
22+
/*struct Expected expected;*/
23+
};
24+
struct CharClass {
25+
/** Старшая половина -- количество элементов в массиве single, младшая -- количество пар
26+
в массиве range.
27+
*/
28+
unsigned int counts;
29+
/** Массив одиночных символов, включенный в данный набор. */
30+
const char* single;
31+
/** Массив пар символов (т.е. длина массива всегда четная), представляющих в данном наборе
32+
поддиапазоны от первого до посленего символа (обе границы включительно).
33+
*/
34+
const char* range;
35+
/** Сообщение об ожидаемом элементе, если сопоставление с классом символов пройдет неудачно. */
36+
/*struct Expected expected;*/
37+
};
38+
/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
39+
/* Вспомогательные структуры. */
40+
/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
41+
typedef struct Result* ResultPtr;
42+
typedef struct Result* (*RuleFunc)(struct Context*);
43+
struct ParseFunc {
44+
/** Длина имени функции для разбора правила. */
45+
unsigned int len;
46+
/** Имя функции для разбора правила. */
47+
const char* name;
48+
/** Функция для разбора правила. */
49+
RuleFunc func;
50+
};
51+
52+
/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
53+
/* Процедуры сопоставления. */
54+
/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
55+
int matchLiteral(struct Context* context, const struct Literal* literal) {
56+
const char* begin;
57+
unsigned int inputLen;
58+
begin = context->input.begin + context->current.offset;
59+
inputLen = context->input.end - begin;
60+
/* Если входная строка короче, то она точно не равна литералу. */
61+
return inputLen < literal->len ? 0 : memcmp(begin, literal->data, literal->len) == 0;
62+
}
63+
/**
64+
@param context Информация о разбираемом участке и текущей в нем позиции.
65+
@param ranges Массив из @a count описателей диапазонов символов/списков символов.
66+
@param count Количество элементов в массиве @a ranges.
67+
*/
68+
int matchCharClass(struct Context* context, struct CharClass* cls, int inverted) {
69+
const char* begin;
70+
assert(context);
71+
assert(context->input.begin);
72+
assert(cls);
73+
assert(cls->single);
74+
assert(cls->range);
75+
76+
begin = context->input.begin + context->current.offset;
77+
78+
/* Если мы еще не в конце входных данных, то пытаемся сматчить текущий символ. */
79+
if (begin < context->input.end) {
80+
/* Выделяем нижнюю половину бит числа с помощью маски. Верхнюю половину получаем, просто
81+
отодвинув ненужную нижнюю половину. */
82+
unsigned int countLO;
83+
unsigned int countHI;
84+
char ch;
85+
unsigned int i;
86+
87+
countLO = cls->counts & ((~0u) >> (sizeof(cls->counts)*4));
88+
countHI = cls->counts >> (sizeof(cls->counts)*4);
89+
ch = begin[0];
90+
/* Класс символов является списком пар, задающих диапазоны символов. */
91+
for (i = 0; i < countLO; ++i) {
92+
char b;
93+
char e;
94+
95+
b = cls->range[i*2];
96+
e = cls->range[i*2 + 1];
97+
/* Строгая проверка, т.к. если начало и конец совпадают, то это единственный символ
98+
и он должен быть в cls->single. */
99+
assert(b < e);
100+
if (b <= ch && ch <= e) {
101+
/* Текущий символ принадлежит диапазону символов. */
102+
return !inverted;
103+
}
104+
}
105+
/* Класс символов является просто списком символов, которые нужно проверить. */
106+
if (memchr(cls->single, ch, countHI) != 0) {
107+
/* Если текущий символ строки имеется в списке допустимых символов, сопоставление успешно. */
108+
return !inverted;
109+
}
110+
}
111+
return inverted;
112+
}
113+
/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
114+
/* Работа с памятью. */
115+
/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
116+
struct Result* allocResult(const char* begin, const char* end, unsigned int count) {
117+
struct Result* r = (struct Result*)malloc(sizeof(struct Result));
118+
assert(begin);
119+
assert(end);
120+
assert(r);
121+
r->region.begin.data = begin;
122+
r->region.end.data = end;
123+
r->count = count;
124+
r->childs = count == 0 ? 0 : calloc(count, sizeof(struct Result));
125+
return r;
126+
}
127+
/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
128+
/* Работа с ожидаемыми элементами. */
129+
/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
130+
void clearExpected(struct FailInfo* info) {
131+
assert(info);
132+
free(info->expected);
133+
info->count = 0;
134+
info->expected = 0;
135+
}
136+
void pushExpected(struct FailInfo* info, struct Expected* expected) {
137+
unsigned int count;
138+
assert(info);
139+
assert(expected);
140+
141+
count = ++info->count;
142+
info->expected = (const struct Expected**)realloc(info->expected, count * sizeof(*info->expected));
143+
info->expected[count-1] = expected;
144+
}
145+
struct Result* fail(struct Context* context, struct Expected* expected) {
146+
assert(context);
147+
assert(expected);
148+
/* Если подавление запоминания позиций ошибки разбора не включено, то пытаемся
149+
запомнить позицию, если она расположена во входных данных позже, чем уже имеющиеся.
150+
*/
151+
if (context->failInfo.silent == 0) {
152+
if (context->current.offset < context->failInfo.pos.offset) { return &FAILED; }
153+
154+
if (context->current.offset > context->failInfo.pos.offset) {
155+
memcpy(&context->failInfo.pos, &context->current, sizeof(struct Location));
156+
clearExpected(&context->failInfo);
157+
}
158+
159+
pushExpected(&context->failInfo, expected);
160+
}
161+
return &FAILED;
162+
}
163+
/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
164+
/* Работа с позицией. */
165+
/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
166+
void movePos(struct Context* context, unsigned int count) {
167+
context->current.data += count;
168+
context->current.offset += count;
169+
170+
/*TODO: Скорректировать line и column*/
171+
}
172+
/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
173+
/* Правила разбора примитивов. */
174+
/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
175+
/** Если в разбираемых данных еще не достигнут конец, продвигает текущую позицию на один символ
176+
вперед и возвращает результат с границами от текущей позиции до текущей позиции плюс 1.
177+
В случае неудачи позиция остается неизменной и возвращается константа FAILED.
178+
179+
@param context Информация о разбираемом участке и текущей в нем позиции.
180+
181+
@return Результат разбора или константу FAILED, если разбор неудачен. Результат необходимо
182+
освободить функцией freeResult, когда он больше не будет нужен.
183+
*/
184+
struct Result* parseAny(struct Context* context) {
185+
#define MAKE_TYPEANDLEN(type, len) ((type << (sizeof(((struct Expected*)0)->typeAndLen)*8 - 3)) | len)
186+
static struct Expected expected = {
187+
MAKE_TYPEANDLEN(E_EX_TYPE_ANY, sizeof("any character") / sizeof(char)),
188+
"any character"
189+
};
190+
#undef MAKE_TYPEANDLEN
191+
const char* begin = context->input.begin + context->current.offset;
192+
193+
if (begin < context->input.end) {
194+
movePos(context, 1);
195+
return allocResult(begin, begin + 1, 0);
196+
} else {
197+
return fail(context, &expected);
198+
}
199+
}
200+
/** Если указанная строка @a literal является подстрокой разбираемых данных, начиная с текущей
201+
позиции разбора, то продвигает текущую позицию на величину @a len и возвращает результат с
202+
границами от текущей позиции до текущей позиции плюс @a len.
203+
В случае неудачи позиция остается неизменной и возвращается константа FAILED.
204+
205+
@param context Информация о разбираемом участке и текущей в нем позиции.
206+
@param literal Литерал, с которым осуществляется сопоставление разбираемых данных.
207+
208+
@return Результат разбора или константу FAILED, если разбор неудачен. Результат необходимо
209+
освободить функцией freeResult, когда он больше не будет нужен.
210+
*/
211+
struct Result* parseLiteral(struct Context* context, struct Literal* literal, struct Expected* expected) {
212+
assert(context);
213+
assert(literal);
214+
assert(expected);
215+
if (matchLiteral(context, literal)) {
216+
const char* begin = context->input.begin + context->current.offset;
217+
movePos(context, literal->len);
218+
return allocResult(begin, begin + literal->len, 0);
219+
} else {
220+
return fail(context, expected);
221+
}
222+
}
223+
struct Result* parseCharClass(struct Context* context, struct CharClass* cls, struct Expected* expected, int inverted) {
224+
assert(context);
225+
assert(cls);
226+
assert(expected);
227+
if (matchCharClass(context, cls, inverted)) {
228+
const char* begin = context->input.begin + context->current.offset;
229+
movePos(context, 1);
230+
return allocResult(begin, begin + 1, 0);
231+
} else {
232+
return fail(context, expected);
233+
}
234+
}
235+
/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
236+
struct Result* wrap(struct Context* context, unsigned int pos, unsigned int count, ...) {
237+
struct Result* r;
238+
unsigned int i;
239+
240+
va_list results;
241+
va_start(results, count);
242+
243+
r = allocResult(context->input.begin + pos, context->input.begin + context->current.offset, count);
244+
for (i = 0; i < count; ++i) {
245+
assert(r->childs);
246+
r->childs[i] = va_arg(results, struct Result*);
247+
assert(r->childs[i]);
248+
}
249+
250+
va_end(results);
251+
return r;
252+
}
253+
static int findRuleCompatator(const struct Range* name, const struct ParseFunc* entry) {
254+
unsigned int len;
255+
assert(name);
256+
assert(name->begin);
257+
assert(name->end);
258+
assert(entry);
259+
len = name->end - name->begin;
260+
if (len < entry->len) { return -1; }
261+
if (len > entry->len) { return +1; }
262+
return memcmp(name->begin, entry->name, len);
263+
}
264+
const struct ParseFunc* findRule(const struct ParseFunc* table, size_t count, const struct Range* name) {
265+
typedef int (*Comparator)(const void*, const void*);
266+
assert(table);
267+
assert(name);
268+
return (const struct ParseFunc*)bsearch(
269+
name, table, count, sizeof(table[0]),
270+
(Comparator)&findRuleCompatator
271+
);
272+
}

0 commit comments

Comments
 (0)