1# LE Grammar for LE Grammars
2#
3# Copyright (c) 2007 by Ian Piumarta
4# All rights reserved.
5#
6# Permission is hereby granted, free of charge, to any person obtaining a
7# copy of this software and associated documentation files (the 'Software'),
8# to deal in the Software without restriction, including without limitation
9# the rights to use, copy, modify, merge, publish, distribute, and/or sell
10# copies of the Software, and to permit persons to whom the Software is
11# furnished to do so, provided that the above copyright notice(s) and this
12# permission notice appear in all copies of the Software.  Acknowledgement
13# of the use of this Software in supporting documentation would be
14# appreciated but is not required.
15#
16# THE SOFTWARE IS PROVIDED 'AS IS'.  USE ENTIRELY AT YOUR OWN RISK.
17#
18# Last edited: 2012-04-29 15:51:15 by piumarta on emilia
19
20%{
21# include "tree.h"
22# include "version.h"
23
24# include <stdio.h>
25# include <stdlib.h>
26# include <unistd.h>
27# include <string.h>
28# include <libgen.h>
29# include <assert.h>
30
31  typedef struct Header Header;
32
33  struct Header {
34    char   *text;
35    Header *next;
36  };
37
38  FILE *input= 0;
39
40  int   verboseFlag= 0;
41
42  static int	 lineNumber= 0;
43  static char	*fileName= 0;
44  static char	*trailer= 0;
45  static Header	*headers= 0;
46
47  void makeHeader(char *text);
48  void makeTrailer(char *text);
49
50  void yyerror(char *message);
51
52# define YY_INPUT(buf, result, max)		\
53  {						\
54    int c= getc(input);				\
55    if ('\n' == c || '\r' == c) ++lineNumber;	\
56    result= (EOF == c) ? 0 : (*(buf)= c, 1);	\
57  }
58
59# define YY_LOCAL(T)	static T
60# define YY_RULE(T)	static T
61%}
62
63# Hierarchical syntax
64
65grammar=	- ( declaration | definition )+ trailer? end-of-file
66
67declaration=	'%{' < ( !'%}' . )* > RPERCENT		{ makeHeader(yytext); }						#{YYACCEPT}
68
69trailer=	'%%' < .* >				{ makeTrailer(yytext); }					#{YYACCEPT}
70
71definition=	identifier 				{ if (push(beginRule(findRule(yytext)))->rule.expression)
72							    fprintf(stderr, "rule '%s' redefined\n", yytext); }
73			EQUAL expression		{ Node *e= pop();  Rule_setExpression(pop(), e); }
74			SEMICOLON?											#{YYACCEPT}
75
76expression=	sequence (BAR sequence			{ Node *f= pop();  push(Alternate_append(pop(), f)); }
77			    )*
78
79sequence=	prefix (prefix				{ Node *f= pop();  push(Sequence_append(pop(), f)); }
80			  )*
81
82prefix=		AND action				{ push(makePredicate(yytext)); }
83|		AND suffix				{ push(makePeekFor(pop())); }
84|		NOT suffix				{ push(makePeekNot(pop())); }
85|		    suffix
86
87suffix=		primary (QUESTION			{ push(makeQuery(pop())); }
88			     | STAR			{ push(makeStar (pop())); }
89			     | PLUS			{ push(makePlus (pop())); }
90			   )?
91
92primary=	identifier				{ push(makeVariable(yytext)); }
93			COLON identifier !EQUAL		{ Node *name= makeName(findRule(yytext));  name->name.variable= pop();  push(name); }
94|		identifier !EQUAL			{ push(makeName(findRule(yytext))); }
95|		OPEN expression CLOSE
96|		literal					{ push(makeString(yytext)); }
97|		class					{ push(makeClass(yytext)); }
98|		DOT					{ push(makeDot()); }
99|		action					{ push(makeAction(yytext)); }
100|		BEGIN					{ push(makePredicate("YY_BEGIN")); }
101|		END					{ push(makePredicate("YY_END")); }
102
103# Lexical syntax
104
105identifier=	< [-a-zA-Z_][-a-zA-Z_0-9]* > -
106
107literal=	['] < ( !['] char )* > ['] -
108|		["] < ( !["] char )* > ["] -
109
110class=		'[' < ( !']' range )* > ']' -
111
112range=		char '-' char | char
113
114char=		'\\' [-abefnrtv'"\[\]\\]
115|		'\\' [0-3][0-7][0-7]
116|		'\\' [0-7][0-7]?
117|		!'\\' .
118
119action=		'{' < braces* > '}' -
120
121braces=		'{' braces* '}'
122|		!'}' .
123
124EQUAL=		'=' -
125COLON=		':' -
126SEMICOLON=	';' -
127BAR=		'|' -
128AND=		'&' -
129NOT=		'!' -
130QUESTION=	'?' -
131STAR=		'*' -
132PLUS=		'+' -
133OPEN=		'(' -
134CLOSE=		')' -
135DOT=		'.' -
136BEGIN=		'<' -
137END=		'>' -
138RPERCENT=	'%}' -
139
140-=		(space | comment)*
141space=		' ' | '\t' | end-of-line
142comment=	'#' (!end-of-line .)* end-of-line
143end-of-line=	'\r\n' | '\n' | '\r'
144end-of-file=	!.
145
146%%
147
148void yyerror(char *message)
149{
150  fprintf(stderr, "%s:%d: %s", fileName, lineNumber, message);
151  if (yyctx->text[0]) fprintf(stderr, " near token '%s'", yyctx->text);
152  if (yyctx->pos < yyctx->limit || !feof(input))
153    {
154      yyctx->buf[yyctx->limit]= '\0';
155      fprintf(stderr, " before text \"");
156      while (yyctx->pos < yyctx->limit)
157	{
158	  if ('\n' == yyctx->buf[yyctx->pos] || '\r' == yyctx->buf[yyctx->pos]) break;
159	  fputc(yyctx->buf[yyctx->pos++], stderr);
160	}
161      if (yyctx->pos == yyctx->limit)
162	{
163	  int c;
164	  while (EOF != (c= fgetc(input)) && '\n' != c && '\r' != c)
165	    fputc(c, stderr);
166	}
167      fputc('\"', stderr);
168    }
169  fprintf(stderr, "\n");
170  exit(1);
171}
172
173void makeHeader(char *text)
174{
175  Header *header= (Header *)malloc(sizeof(Header));
176  header->text= strdup(text);
177  header->next= headers;
178  headers= header;
179}
180
181void makeTrailer(char *text)
182{
183  trailer= strdup(text);
184}
185
186static void version(char *name)
187{
188  printf("%s version %d.%d.%d\n", name, PEG_MAJOR, PEG_MINOR, PEG_LEVEL);
189}
190
191static void usage(char *name)
192{
193  version(name);
194  fprintf(stderr, "usage: %s [<option>...] [<file>...]\n", name);
195  fprintf(stderr, "where <option> can be\n");
196  fprintf(stderr, "  -h          print this help information\n");
197  fprintf(stderr, "  -o <ofile>  write output to <ofile>\n");
198  fprintf(stderr, "  -v          be verbose\n");
199  fprintf(stderr, "  -V          print version number and exit\n");
200  fprintf(stderr, "if no <file> is given, input is read from stdin\n");
201  fprintf(stderr, "if no <ofile> is given, output is written to stdout\n");
202  exit(1);
203}
204
205int main(int argc, char **argv)
206{
207  Node *n;
208  int   c;
209
210  output= stdout;
211  input= stdin;
212  lineNumber= 1;
213  fileName= "<stdin>";
214
215  while (-1 != (c= getopt(argc, argv, "Vho:v")))
216    {
217      switch (c)
218	{
219	case 'V':
220	  version(basename(argv[0]));
221	  exit(0);
222
223	case 'h':
224	  usage(basename(argv[0]));
225	  break;
226
227	case 'o':
228	  if (!(output= fopen(optarg, "w")))
229	    {
230	      perror(optarg);
231	      exit(1);
232	    }
233	  break;
234
235	case 'v':
236	  verboseFlag= 1;
237	  break;
238
239	default:
240	  fprintf(stderr, "for usage try: %s -h\n", argv[0]);
241	  exit(1);
242	}
243    }
244  argc -= optind;
245  argv += optind;
246
247  if (argc)
248    {
249      for (;  argc;  --argc, ++argv)
250	{
251	  if (!strcmp(*argv, "-"))
252	    {
253	      input= stdin;
254	      fileName= "<stdin>";
255	    }
256	  else
257	    {
258	      if (!(input= fopen(*argv, "r")))
259		{
260		  perror(*argv);
261		  exit(1);
262		}
263	      fileName= *argv;
264	    }
265	  lineNumber= 1;
266	  if (!yyparse())
267	    yyerror("syntax error");
268	  if (input != stdin)
269	    fclose(input);
270	}
271    }
272  else
273    if (!yyparse())
274      yyerror("syntax error");
275
276  if (verboseFlag)
277    for (n= rules;  n;  n= n->any.next)
278      Rule_print(n);
279
280  Rule_compile_c_header();
281
282  for (; headers;  headers= headers->next)
283    fprintf(output, "%s\n", headers->text);
284
285  if (rules)
286    Rule_compile_c(rules);
287
288  if (trailer)
289    fprintf(output, "%s\n", trailer);
290
291  return 0;
292}
293