1 /**
2 
3 	MultiMarkdown 6 -- Lightweight markup processor to produce HTML, LaTeX, and more.
4 
5 	@file parser.y
6 
7 	@brief Definition of the parser grammar, processed with lemon to create a parser.
8 
9 		http://www.hwaci.com/sw/lemon/
10 
11 	@author	Fletcher T. Penney
12 	@bug
13 
14 **/
15 
16 /*
17 
18 	Copyright © 2016 - 2017 Fletcher T. Penney.
19 
20 
21 	The `MultiMarkdown 6` project is released under the MIT License..
22 
23 	GLibFacade.c and GLibFacade.h are from the MultiMarkdown v4 project:
24 
25 		https://github.com/fletcher/MultiMarkdown-4/
26 
27 	MMD 4 is released under both the MIT License and GPL.
28 
29 
30 	CuTest is released under the zlib/libpng license. See CuTest.c for the text
31 	of the license.
32 
33 
34 	## The MIT License ##
35 
36 	Permission is hereby granted, free of charge, to any person obtaining a copy
37 	of this software and associated documentation files (the "Software"), to deal
38 	in the Software without restriction, including without limitation the rights
39 	to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
40 	copies of the Software, and to permit persons to whom the Software is
41 	furnished to do so, subject to the following conditions:
42 
43 	The above copyright notice and this permission notice shall be included in
44 	all copies or substantial portions of the Software.
45 
46 	THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
47 	IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
48 	FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
49 	AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
50 	LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
51 	OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
52 	THE SOFTWARE.
53 
54 */
55 
56 
57 //
58 // Language grammar here
59 //
60 
61 %token_type { token * }
62 
63 %extra_argument { mmd_engine * engine }
64 
65 %fallback LINE_HR LINE_SETEXT_1 LINE_SETEXT_2 LINE_YAML.
66 
67 //%fallback LINE_PLAIN LINE_TABLE_SEPARATOR.
68 
69 %fallback LINE_CONTINUATION LINE_PLAIN LINE_INDENTED_TAB LINE_INDENTED_SPACE LINE_TABLE LINE_TABLE_SEPARATOR.
70 
71 %fallback LINE_FALLBACK LINE_HTML LINE_ATX_1 LINE_ATX_2 LINE_ATX_3 LINE_ATX_4 LINE_ATX_5 LINE_ATX_6
72 LINE_BLOCKQUOTE LINE_LIST_BULLETED LINE_LIST_ENUMERATED LINE_DEF_ABBREVIATION LINE_DEF_CITATION
73 LINE_DEF_FOOTNOTE LINE_DEF_GLOSSARY LINE_DEF_LINK LINE_TOC LINE_DEFINITION LINE_META.
74 
75 %fallback LINE_BACKTICK LINE_FENCE_BACKTICK_3 LINE_FENCE_BACKTICK_4 LINE_FENCE_BACKTICK_5
76 LINE_FENCE_BACKTICK_START_3 LINE_FENCE_BACKTICK_START_4 LINE_FENCE_BACKTICK_START_5.
77 
78 
79 doc					::= blocks(B).								{ engine->root = B; }
80 
blocks(A)81 blocks(A)			::= blocks(B) block(C).
82 	{
83 		strip_line_tokens_from_block(engine, C);
84 		if (B == NULL) { B = C; C = NULL;}
85 		A = B;
86 		token_chain_append(A, C);
87 	}
blocks(A)88 blocks(A)			::= block(B).
89 	{
90 		engine->root = B;	// In case the first block is metadata and we just want to know if it exists
91 		strip_line_tokens_from_block(engine, B);
92 		A = B;
93 	}
94 
95 
96 // Blocks
97 
98 // Single line blocks
99 
block(A)100 block(A)			::= LINE_ATX_1(B).			{ A = token_new_parent(B, BLOCK_H1); stack_push(engine->header_stack, A); }
block(A)101 block(A)			::= LINE_ATX_2(B).			{ A = token_new_parent(B, BLOCK_H2); stack_push(engine->header_stack, A); }
block(A)102 block(A)			::= LINE_ATX_3(B).			{ A = token_new_parent(B, BLOCK_H3); stack_push(engine->header_stack, A); }
block(A)103 block(A)			::= LINE_ATX_4(B).			{ A = token_new_parent(B, BLOCK_H4); stack_push(engine->header_stack, A); }
block(A)104 block(A)			::= LINE_ATX_5(B).			{ A = token_new_parent(B, BLOCK_H5); stack_push(engine->header_stack, A); }
block(A)105 block(A)			::= LINE_ATX_6(B).			{ A = token_new_parent(B, BLOCK_H6); stack_push(engine->header_stack, A); }
106 
block(A)107 block(A)			::= LINE_HR(B).				{ A = token_new_parent(B, BLOCK_HR); }
block(A)108 block(A)			::= LINE_YAML(B).			{ A = token_new_parent(B, BLOCK_HR); }
109 
block(A)110 block(A)			::= LINE_TOC(B).			{ A = token_new_parent(B, BLOCK_TOC); }
111 
112 
113 // Multi-line blocks
114 
block(A)115 block(A)			::= blockquote(B).			{ A = token_new_parent(B, BLOCK_BLOCKQUOTE); recursive_parse_blockquote(engine, A); }
block(A)116 block(A)			::= def_abbreviation(B).	{ A = token_new_parent(B, BLOCK_DEF_ABBREVIATION); stack_push(engine->definition_stack, A); }
block(A)117 block(A)			::= def_citation(B).		{ A = token_new_parent(B, BLOCK_DEF_CITATION); stack_push(engine->definition_stack, A); }
block(A)118 block(A)			::= def_footnote(B).		{ A = token_new_parent(B, BLOCK_DEF_FOOTNOTE); stack_push(engine->definition_stack, A); recursive_parse_indent(engine, A); }
block(A)119 block(A)			::= def_glossary(B).		{ A = token_new_parent(B, BLOCK_DEF_GLOSSARY); stack_push(engine->definition_stack, A); recursive_parse_indent(engine, A); }
block(A)120 block(A)			::= def_link(B).			{ A = token_new_parent(B, BLOCK_DEF_LINK); stack_push(engine->definition_stack, A); }
block(A)121 block(A)			::= definition_block(B).	{ A = token_new_parent(B, BLOCK_DEFLIST); }
block(A)122 block(A)			::= empty(B).				{ A = token_new_parent(B, BLOCK_EMPTY); }
block(A)123 block(A)			::= fenced_block(B).		{ A = token_new_parent(B, BLOCK_CODE_FENCED); B->child->type = CODE_FENCE; }
block(A)124 block(A)			::= html_block(B).			{ A = token_new_parent(B, BLOCK_HTML); }
block(A)125 block(A)			::= html_com_block(B).		{ A = token_new_parent(B, BLOCK_HTML); }
block(A)126 block(A)			::= indented_code(B).		{ A = token_new_parent(B, BLOCK_CODE_INDENTED); }
block(A)127 block(A)			::= list_bullet(B).			{ A = token_new_parent(B, BLOCK_LIST_BULLETED); is_list_loose(A); }
block(A)128 block(A)			::= list_enum(B).			{ A = token_new_parent(B, BLOCK_LIST_ENUMERATED); is_list_loose(A); }
block(A)129 block(A)			::= meta_block(B).			{ A = token_new_parent(B, BLOCK_META); }
block(A)130 block(A)			::= meta_block(B) LINE_SETEXT_2(C).	{ A = token_new_parent(B, BLOCK_META); token_append_child(A, C); }
block(A)131 block(A)			::= para(B).				{ A = token_new_parent(B, BLOCK_PARA); is_para_html(engine, A); }
block(A)132 block(A)			::= setext_1(B).			{ A = token_new_parent(B, BLOCK_SETEXT_1); stack_push(engine->header_stack, A); }
block(A)133 block(A)			::= setext_2(B).			{ A = token_new_parent(B, BLOCK_SETEXT_2); stack_push(engine->header_stack, A); }
block(A)134 block(A)			::= table(B).				{ A = token_new_parent(B, BLOCK_TABLE); stack_push(engine->table_stack, A); }
135 
136 
137 // Reusable components
138 
139 // A "chunk" allows you to grab all regular lines before the next empty line.
140 // For example, to grab all lines after the first line in a paragraph.
141 // You will want to specify a certain line type to start a chunk, but
142 // this can grab the remaining lines, if any.
143 
chunk(A)144 chunk(A)			::= chunk(B) chunk_line(C).					{ A = B; token_chain_append(B, C); }
145 chunk				::= chunk_line.
146 
147 chunk_line			::= LINE_CONTINUATION.
148 chunk_line			::= LINE_STOP_COMMENT.
149 
150 
151 // A "nested chunk" is useful when a chunk can also include following blocks
152 // that have an extra level of indention to indicate that they belong together.
153 // For example, a list item can include multiple paragraphs, even other lists.
154 // This structure is also used in footnotes and definitions.
155 
nested_chunks(A)156 nested_chunks(A)	::= nested_chunks(B) nested_chunk(C).		{ A = B; token_chain_append(B, C); }
157 nested_chunks		::= nested_chunk.
158 
nested_chunk(A)159 nested_chunk(A)		::= empty(B) indented_line(C) chunk(D).		{ A = B; token_chain_append(B, C); token_chain_append(B, D); C->type = LINE_CONTINUATION; }
nested_chunk(A)160 nested_chunk(A)		::= empty(B) indented_line(C).				{ A = B; token_chain_append(B, C); C->type = LINE_CONTINUATION; }
161 nested_chunk		::= empty.
162 
163 indented_line		::= LINE_INDENTED_TAB.
164 indented_line		::= LINE_INDENTED_SPACE.
165 
166 
167 // Shortcut for optional chunk
168 //opt_chunk			::= chunk.
169 //opt_chunk			::= .
170 
171 
172 // Shortcut for "extended chunk" (chunk with following blocks)
ext_chunk(A)173 ext_chunk(A)		::= chunk(B) nested_chunks(C).				{ A = B; token_chain_append(B, C); }
174 
175 
176 // Shortcut for optionally extended chunk
opt_ext_chunk(A)177 opt_ext_chunk(A)	::= chunk(B) nested_chunks(C).				{ A = B; token_chain_append(B, C); }
178 opt_ext_chunk		::= chunk.
179 
180 
181 // Shortcut for anything that falls into the extended chunk pattern
182 tail				::= opt_ext_chunk.
183 tail				::= nested_chunks.
184 //tail 				::= empty.
185 
186 
187 // Blockquotes
blockquote(A)188 blockquote(A)		::= blockquote(B) quote_line(C).			{ A = B; token_chain_append(B, C); }
189 blockquote 			::= LINE_BLOCKQUOTE.
190 
191 quote_line			::= LINE_BLOCKQUOTE.
192 quote_line			::= LINE_CONTINUATION.
193 
194 
195 // Reference definitions
def_citation(A)196 def_citation(A)		::= LINE_DEF_CITATION(B) tail(C).			{ A = B; token_chain_append(B, C); }
197 def_citation		::= LINE_DEF_CITATION.
198 
def_footnote(A)199 def_footnote(A)		::= LINE_DEF_FOOTNOTE(B) tail(C).			{ A = B; token_chain_append(B, C); }
200 def_footnote		::= LINE_DEF_FOOTNOTE.
201 
def_glossary(A)202 def_glossary(A)		::= LINE_DEF_GLOSSARY(B) tail(C).			{ A = B; token_chain_append(B, C); }
203 def_glossary		::= LINE_DEF_GLOSSARY.
204 
def_link(A)205 def_link(A)			::= LINE_DEF_LINK(B) chunk(C).				{ A = B; token_chain_append(B, C); }
206 def_link			::= LINE_DEF_LINK.
207 
def_abbreviation(A)208 def_abbreviation(A)	::= LINE_DEF_ABBREVIATION(B) chunk(C).		{ A = B; token_chain_append(B, C); }
209 def_abbreviation	::= LINE_DEF_ABBREVIATION.
210 
211 
212 // Definition lists
213 // Lemon's LALR(1) parser can't properly allow for detecting consecutive definition blocks and concatenating them,
214 // because 'para defs para' could be the beginning of the next definition, OR the next regular para.
215 // We have to bundle them when exporting, if desired.
definition_block(A)216 definition_block(A)	::= para(B) defs(C).						{ A = B; token_chain_append(B, C); B->type = BLOCK_TERM; }
217 
defs(A)218 defs(A)				::= defs(B) def(C).							{ A = B; token_chain_append(B, C); }
219 defs				::= def.
220 
def(A)221 def(A)				::= LINE_DEFINITION(B) tail(C).				{ A = token_new_parent(B, BLOCK_DEFINITION); token_chain_append(B, C); recursive_parse_indent(engine, A); }
def(A)222 def(A)				::= LINE_DEFINITION(B).						{ A = token_new_parent(B, BLOCK_DEFINITION);  }
223 
224 
225 // Empty lines
empty(A)226 empty(A)			::= empty(B) LINE_EMPTY(C).					{ A = B; token_chain_append(B, C); }
227 empty				::= LINE_EMPTY.
228 
229 
230 // Fenced code blocks
231 
fenced_block(A)232 fenced_block(A)		::= fenced_3(B) LINE_FENCE_BACKTICK_3(C).	{ A = B; token_chain_append(B, C); C->child->type = CODE_FENCE; }
fenced_block(A)233 fenced_block(A)		::= fenced_3(B) LINE_FENCE_BACKTICK_4(C).	{ A = B; token_chain_append(B, C); C->child->type = CODE_FENCE; }
fenced_block(A)234 fenced_block(A)		::= fenced_3(B) LINE_FENCE_BACKTICK_5(C).	{ A = B; token_chain_append(B, C); C->child->type = CODE_FENCE; }
235 fenced_block		::= fenced_3.
236 
fenced_3(A)237 fenced_3(A)			::= fenced_3(B) fenced_line(C).				{ A = B; token_chain_append(B, C); }
238 fenced_3			::= LINE_FENCE_BACKTICK_3.
239 fenced_3			::= LINE_FENCE_BACKTICK_START_3.
240 
241 
fenced_block(A)242 fenced_block(A)		::= fenced_4(B) LINE_FENCE_BACKTICK_4(C).	{ A = B; token_chain_append(B, C); C->child->type = CODE_FENCE; }
fenced_block(A)243 fenced_block(A)		::= fenced_4(B) LINE_FENCE_BACKTICK_5(C).	{ A = B; token_chain_append(B, C); C->child->type = CODE_FENCE; }
244 fenced_block		::= fenced_4.
245 
fenced_4(A)246 fenced_4(A)			::= fenced_4(B) fenced_line(C).				{ A = B; token_chain_append(B, C); }
fenced_4(A)247 fenced_4(A)			::= fenced_4(B) LINE_FENCE_BACKTICK_3(C).	{ A = B; token_chain_append(B, C); }
fenced_4(A)248 fenced_4(A)			::= fenced_4(B) LINE_FENCE_BACKTICK_START_3(C).	{ A = B; token_chain_append(B, C); }
249 fenced_4			::= LINE_FENCE_BACKTICK_4.
250 fenced_4			::= LINE_FENCE_BACKTICK_START_4.
251 
252 
fenced_block(A)253 fenced_block(A)		::= fenced_5(B) LINE_FENCE_BACKTICK_5(C).	{ A = B; token_chain_append(B, C); C->child->type = CODE_FENCE; }
254 fenced_block		::= fenced_5.
255 
fenced_5(A)256 fenced_5(A)			::= fenced_5(B) fenced_line(C).				{ A = B; token_chain_append(B, C); }
fenced_5(A)257 fenced_5(A)			::= fenced_5(B) LINE_FENCE_BACKTICK_3(C).	{ A = B; token_chain_append(B, C); }
fenced_5(A)258 fenced_5(A)			::= fenced_5(B) LINE_FENCE_BACKTICK_START_3(C).	{ A = B; token_chain_append(B, C); }
fenced_5(A)259 fenced_5(A)			::= fenced_5(B) LINE_FENCE_BACKTICK_4(C).	{ A = B; token_chain_append(B, C); }
fenced_5(A)260 fenced_5(A)			::= fenced_5(B) LINE_FENCE_BACKTICK_START_4(C).	{ A = B; token_chain_append(B, C); }
261 fenced_5			::= LINE_FENCE_BACKTICK_5.
262 fenced_5			::= LINE_FENCE_BACKTICK_START_5.
263 
264 fenced_line			::= LINE_CONTINUATION.
265 fenced_line			::= LINE_EMPTY.
266 fenced_line			::= LINE_FALLBACK.
267 fenced_line			::= LINE_HR.
268 fenced_line			::= LINE_HTML.
269 fenced_line			::= LINE_START_COMMENT.
270 fenced_line			::= LINE_STOP_COMMENT.
271 
272 
273 // HTML
html_block(A)274 html_block(A)		::= html_block(B) html_line(C).				{ A = B; token_chain_append(B, C); }
275 html_block			::= LINE_HTML.
276 
277 html_line			::= LINE_CONTINUATION.
278 html_line			::= LINE_FALLBACK.
279 html_line			::= LINE_HR.
280 html_line			::= LINE_HTML.
281 
282 
283 // HTML Comment
html_com_block(A)284 html_com_block(A)	::= html_comment(B) LINE_STOP_COMMENT(C).	{ A = B; token_chain_append(B, C); }
285 html_com_block 		::= html_comment.
286 
html_comment(A)287 html_comment(A)		::= html_comment(B) comment_line(C).		{ A = B; token_chain_append(B, C); }
288 html_comment		::= LINE_START_COMMENT.
289 
290 comment_line		::= LINE_CONTINUATION.
291 comment_line		::= LINE_EMPTY.
292 comment_line		::= LINE_FALLBACK.
293 comment_line		::= LINE_HR.
294 comment_line		::= LINE_HTML.
295 
296 
297 // Indented code blocks
indented_code(A)298 indented_code(A)	::= indented_code(B) indented_line(C).		{ A = B; token_chain_append(B, C); }
indented_code(A)299 indented_code(A)	::= indented_code(B) LINE_EMPTY(C).			{ A = B; token_chain_append(B, C); }
300 indented_code		::= indented_line.
301 
302 
303 // Bulleted lists
list_bullet(A)304 list_bullet(A)		::= list_bullet(B) item_bullet(C).			{ A = B; token_chain_append(B, C); }
305 list_bullet			::=	item_bullet.
306 
item_bullet(A)307 item_bullet(A)		::= LINE_LIST_BULLETED(B) ext_chunk(C).		{ A = token_new_parent(B, BLOCK_LIST_ITEM); token_chain_append(B, C); recursive_parse_list_item(engine, A); }
item_bullet(A)308 item_bullet(A)		::= LINE_LIST_BULLETED(B) chunk(C).			{ A = token_new_parent(B, BLOCK_LIST_ITEM_TIGHT); token_chain_append(B, C); recursive_parse_list_item(engine, A); }
item_bullet(A)309 item_bullet(A)		::= LINE_LIST_BULLETED(B) nested_chunks(C).	{ A = token_new_parent(B, BLOCK_LIST_ITEM); token_chain_append(B, C); recursive_parse_list_item(engine, A); }
item_bullet(A)310 item_bullet(A)		::= LINE_LIST_BULLETED(B).					{ A = token_new_parent(B, BLOCK_LIST_ITEM_TIGHT); }
311 
312 
313 // Enumerated lists
list_enum(A)314 list_enum(A)		::= list_enum(B) item_enum(C).				{ A = B; token_chain_append(B, C); }
315 list_enum			::=	item_enum.
316 
item_enum(A)317 item_enum(A)		::= LINE_LIST_ENUMERATED(B) ext_chunk(C).	{ A = token_new_parent(B, BLOCK_LIST_ITEM); token_chain_append(B, C); recursive_parse_list_item(engine, A); }
item_enum(A)318 item_enum(A)		::= LINE_LIST_ENUMERATED(B) chunk(C).		{ A = token_new_parent(B, BLOCK_LIST_ITEM_TIGHT); token_chain_append(B, C); recursive_parse_list_item(engine, A); }
item_enum(A)319 item_enum(A)		::= LINE_LIST_ENUMERATED(B) nested_chunks(C).	{ A = token_new_parent(B, BLOCK_LIST_ITEM); token_chain_append(B, C); recursive_parse_list_item(engine, A); }
item_enum(A)320 item_enum(A)		::= LINE_LIST_ENUMERATED(B).				{ A = token_new_parent(B, BLOCK_LIST_ITEM_TIGHT); }
321 
322 
323 // Metadata
meta_block(A)324 meta_block(A)		::= meta_block(B) meta_line(C).				{ A = B; token_chain_append(B, C); }
325 meta_block 			::= LINE_META.
meta_block(A)326 meta_block(A)		::= LINE_YAML(B) LINE_META(C).				{ A = B; token_chain_append(B, C); }
327 
328 meta_line 			::= LINE_META.
329 meta_line 			::= LINE_CONTINUATION.
330 meta_line			::= LINE_FALLBACK.
331 
332 
333 // Paragraphs
para(A)334 para(A)				::= LINE_PLAIN(B) chunk(C).					{ A = B; token_chain_append(B, C); }
335 para				::= LINE_PLAIN.
336 para				::= LINE_STOP_COMMENT.
337 
338 
339 // Setext headers
setext_1(A)340 setext_1(A)			::= para(B) LINE_SETEXT_1(C).				{ A = B; token_chain_append(B, C); }
setext_2(A)341 setext_2(A)			::= para(B) LINE_SETEXT_2(C).				{ A = B; token_chain_append(B, C); }
342 
343 
344 // Tables
table(A)345 table(A)			::= table_header(B) table_body(C).			{ A = B; token_chain_append(B, C); }
346 table				::= table_header.
347 
table_header(A)348 table_header(A)		::= header_rows(B) LINE_TABLE_SEPARATOR(C).	{ A = token_new_parent(B, BLOCK_TABLE_HEADER); token_chain_append(B, C); }
table_header(A)349 table_header(A)		::= LINE_TABLE_SEPARATOR(B).				{ A = token_new_parent(B, BLOCK_TABLE_HEADER); }
350 
header_rows(A)351 header_rows(A)		::= header_rows(B) LINE_TABLE(C).			{ A = B; token_chain_append(B, C); }
352 header_rows			::= LINE_TABLE.
353 
table_body(A)354 table_body(A)		::= table_body(B) table_section(C).			{ A = B; token_chain_append(B, C); }
355 table_body			::= table_section.
356 
table_section(A)357 table_section(A)	::= all_rows(B) LINE_EMPTY(C).				{ A = token_new_parent(B, BLOCK_TABLE_SECTION); token_chain_append(B, C); }
table_section(A)358 table_section(A)	::= all_rows(B).							{ A = token_new_parent(B, BLOCK_TABLE_SECTION); }
359 
all_rows(A)360 all_rows(A)			::= all_rows(B) row(C).						{ A = B; token_chain_append(B, C); }
361 all_rows			::= row.
362 
363 row					::= header_rows.
364 row					::= LINE_TABLE_SEPARATOR.
365 
366 
367 // Fallbacks for improper structures
para(A)368 para(A)				::= all_rows(B).							{ A = B; }
369 para				::= defs.
370 
371 
372 //
373 // Additional Configuration
374 //
375 
376 %include {
377 	#include <assert.h>
378 	#include <stdio.h>
379 	#include <stdlib.h>
380 
381 	#include "libMultiMarkdown.h"
382 	#include "mmd.h"
383 	#include "parser.h"
384 	#include "stack.h"
385 	#include "token.h"
386 }
387 
388 
389 // Improved error messages for debugging:
390 //	http://stackoverflow.com/questions/11705737/expected-token-using-lemon-parser-generator
391 
392 %syntax_error {
393 #ifndef NDEBUG
394 	fprintf(stderr,"Parser syntax error.\n");
395 	int n = sizeof(yyTokenName) / sizeof(yyTokenName[0]);
396 	for (int i = 0; i < n; ++i) {
397 		int a = yy_find_shift_action(yypParser, (YYCODETYPE)i);
398 		if (a < YYNSTATE + YYNRULE) {
399 			fprintf(stderr,"expected token: %s\n", yyTokenName[i]);
400 		}
401 	}
402 #endif
403 }
404 
405 %parse_accept {
406 //	printf("parsing completed successfully!\n");
407 }
408 
409 %parse_failure {
410 	fprintf(stderr, "Parser failed to successfully parse.\n");
411 }
412 
413