More struct rearrangement
[mspang/inapt.git] / parser.rl
1 #include <stdio.h>
2 #include <string.h>
3 #include <stdlib.h>
4 #include <unistd.h>
5 #include <fcntl.h>
6 #include <ctype.h>
7 #include <vector>
8
9 #include "inapt.h"
10 #include "util.h"
11
12 using namespace std;
13
14 #define MAXDEPTH 100
15 #define BUFSIZE 128
16
17 %%{
18     machine inapt;
19
20     action strstart { ts = p; }
21
22     action add_alternate {
23         std::string tmp (ts, p - ts); ts = 0;
24         alternates.push_back(tmp);
25     }
26
27     action add_package {
28         inapt_package *tmp_package = new inapt_package;
29         tmp_package->alternates.swap(alternates);
30         tmp_package->action = tmp_action->action;
31         tmp_package->linenum = curline;
32         tmp_package->filename = curfile;
33         tmp_package->predicates.swap(pkg_predicates);
34         tmp_action->packages.push_back(tmp_package);
35     }
36
37     action start_install {
38         tmp_action = new inapt_action;
39         tmp_action->action = inapt_action::INSTALL;
40         tmp_action->predicates.swap(cmd_predicates);
41         block_stack.back()->actions.push_back(tmp_action);
42     }
43
44     action start_remove {
45         tmp_action = new inapt_action;
46         tmp_action->action = inapt_action::REMOVE;
47         tmp_action->predicates.swap(cmd_predicates);
48         block_stack.back()->actions.push_back(tmp_action);
49     }
50
51     action newline {
52         curline += 1;
53     }
54
55     action start_block {
56         if (top < MAXDEPTH) {
57             inapt_block *tmp_block = new inapt_block;
58             block_stack.push_back(tmp_block);
59             fcall main;
60         } else {
61             fatal("%s: %d: Syntax Error: Nesting Too Deep at '}'", curfile, curline);
62         }
63     }
64
65     action end_block {
66         if (top) {
67             fret;
68         } else {
69             fatal("%s: %d: Syntax Error: Unexpected '}'", curfile, curline);
70         }
71     }
72
73     action start_conditional {
74         inapt_conditional *cond = new inapt_conditional;
75         cond->condition = xstrndup(ts, p - ts); ts = 0;
76         conditional_stack.push_back(cond);
77     }
78
79     action full_conditional {
80         inapt_conditional *cond = conditional_stack.back(); conditional_stack.pop_back();
81         cond->else_block = block_stack.back(); block_stack.pop_back();
82         cond->then_block = block_stack.back(); block_stack.pop_back();
83         block_stack.back()->children.push_back(cond);
84     }
85
86     action half_conditional {
87         inapt_conditional *cond = conditional_stack.back(); conditional_stack.pop_back();
88         cond->else_block = NULL;
89         cond->then_block = block_stack.back(); block_stack.pop_back();
90         block_stack.back()->children.push_back(cond);
91     }
92
93     action pkg_predicate {
94         std::string tmp (ts, p - ts); ts = 0;
95         pkg_predicates.push_back(tmp);
96     }
97
98     action cmd_predicate {
99         std::string tmp (ts, p - ts); ts = 0;
100         cmd_predicates.push_back(tmp);
101     }
102
103     newline = '\n' @newline;
104     comment = '#' (any - '\n')* newline;
105     whitespace = [\t\v\f\r ] | comment | newline;
106     macro = '!'? alpha (alpha | digit | '-' | '+' | '.')*;
107     package_name = ((lower | digit) (lower | digit | '+' | '-' | '.')+) >strstart;
108     pkg_predicate = '@' macro >strstart %pkg_predicate whitespace+;
109     cmd_predicate = '@' macro >strstart %cmd_predicate whitespace+;
110     package_alternates = package_name >strstart %add_alternate ('/' package_name >strstart %add_alternate)*;
111     package_list = ((whitespace+ pkg_predicate? package_alternates)+ %add_package whitespace*);
112     cmd_install = ('install' @start_install package_list ';');
113     cmd_remove = ('remove' @start_remove package_list ';');
114     start_block = '{' @start_block;
115     end_block = '}' @end_block;
116     cmd_if = 'if' whitespace+ macro >strstart %start_conditional whitespace* start_block whitespace*
117              ('else' whitespace* start_block whitespace* ';' @full_conditional | ';' @half_conditional);
118     cmd = whitespace* (cmd_predicate? (cmd_install | cmd_remove) | cmd_if);
119     cmd_list = cmd* whitespace* end_block?;
120     main := cmd_list;
121 }%%
122
123 %% write data;
124
125 void badsyntax(const char *filename, int lineno, char badchar, const char *message) {
126     if (!message) {
127         if (badchar == '\n')
128             message = "Unexpected newline";
129         else if (isspace(badchar))
130             message = "Unexpected whitespace";
131         else
132             message = "Syntax error";
133     }
134
135     if (isprint(badchar) && !isspace(badchar))
136         fatal("%s: %d: %s at '%c'", filename, lineno, message, badchar);
137     else
138         fatal("%s: %d: %s", filename, lineno, message);
139 }
140
141 void parser(const char *filename, inapt_block *top_block)
142 {
143     static char buf[BUFSIZE];
144     int fd;
145     int cs, have = 0;
146     int done = 0;
147     int curline = 1;
148     char *ts = 0;
149
150     std::vector<inapt_block *> block_stack;
151     std::vector<inapt_conditional *> conditional_stack;
152     std::vector<std::string> alternates;
153     std::vector<std::string> cmd_predicates;
154     std::vector<std::string> pkg_predicates;
155     block_stack.push_back(top_block);
156     inapt_action *tmp_action = NULL;
157
158     int stack[MAXDEPTH];
159     int top = 0;
160
161     const char *curfile = filename;
162
163     if (filename) {
164         fd = open(filename, O_RDONLY);
165         if (fd < 0)
166             fatalpe("open: %s", filename);
167     } else {
168         curfile = "stdin";
169         fd = 0;
170     }
171
172     %% write init;
173
174     while (!done) {
175         char *p = buf + have, *pe, *eof = 0;
176         int len, space = BUFSIZE - have;
177
178         if (!space)
179             badsyntax(curfile, curline, 0, "Overlength token");
180
181         len = read(fd, p, space);
182         if (len < 0)
183             fatalpe("Unable to read spec");
184         pe = p + len;
185
186         if (!len) {
187             eof = pe;
188             done = 1;
189         }
190
191         %% write exec;
192
193         if (cs == inapt_error)
194             badsyntax(curfile, curline, *p, NULL);
195
196         have = 0;
197
198         if (ts) {
199             have = pe - ts;
200             memmove(buf, ts, have);
201             ts = buf;
202         }
203     }
204
205     if (cs < inapt_first_final)
206         badsyntax(curfile, curline, 0, "Unexpected EOF");
207
208     if (top)
209         badsyntax(curfile, curline, 0, "Unclosed block at EOF");
210 }