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