Add a "profiles" command to select profiles
[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 add_profiles {
52         inapt_profiles *tmp_profiles = new inapt_profiles;
53         tmp_profiles->profiles.swap(profiles);
54         tmp_profiles->predicates.swap(cmd_predicates);
55         block_stack.back()->profiles.push_back(tmp_profiles);
56     }
57
58     action newline {
59         curline += 1;
60     }
61
62     action start_block {
63         if (top < MAXDEPTH) {
64             inapt_block *tmp_block = new inapt_block;
65             block_stack.push_back(tmp_block);
66             fcall main;
67         } else {
68             fatal("%s: %d: Syntax Error: Nesting Too Deep at '}'", curfile, curline);
69         }
70     }
71
72     action end_block {
73         if (top) {
74             fret;
75         } else {
76             fatal("%s: %d: Syntax Error: Unexpected '}'", curfile, curline);
77         }
78     }
79
80     action start_conditional {
81         inapt_conditional *cond = new inapt_conditional;
82         cond->condition = xstrndup(ts, p - ts); ts = 0;
83         conditional_stack.push_back(cond);
84     }
85
86     action full_conditional {
87         inapt_conditional *cond = conditional_stack.back(); conditional_stack.pop_back();
88         cond->else_block = block_stack.back(); block_stack.pop_back();
89         cond->then_block = block_stack.back(); block_stack.pop_back();
90         block_stack.back()->children.push_back(cond);
91     }
92
93     action half_conditional {
94         inapt_conditional *cond = conditional_stack.back(); conditional_stack.pop_back();
95         cond->else_block = NULL;
96         cond->then_block = block_stack.back(); block_stack.pop_back();
97         block_stack.back()->children.push_back(cond);
98     }
99
100     action pkg_predicate {
101         std::string tmp (ts, p - ts); ts = 0;
102         pkg_predicates.push_back(tmp);
103     }
104
105     action cmd_predicate {
106         std::string tmp (ts, p - ts); ts = 0;
107         cmd_predicates.push_back(tmp);
108     }
109
110     action profile {
111         std::string tmp (ts, p - ts); ts = 0;
112         profiles.push_back(tmp);
113     }
114
115     newline = '\n' @newline;
116     comment = '#' (any - '\n')* newline;
117     whitespace = [\t\v\f\r ] | comment | newline;
118     profile = alpha (alpha | digit | '-' | '+' | '.')*;
119     package_name = ((lower | digit) (lower | digit | '+' | '-' | '.')+) >strstart;
120     pkg_predicate = '@' ('!'? profile) >strstart %pkg_predicate whitespace+;
121     cmd_predicate = '@' ('!'? profile) >strstart %cmd_predicate whitespace+;
122     package_alternates = package_name >strstart %add_alternate ('/' package_name >strstart %add_alternate)*;
123     package_list = ((whitespace+ pkg_predicate? package_alternates)+ %add_package whitespace*);
124     profile_list = (whitespace+ profile >strstart %profile)* whitespace*;
125     cmd_install = ('install' @start_install package_list ';');
126     cmd_remove = ('remove' @start_remove package_list ';');
127     cmd_profiles = ('profiles' profile_list ';' @add_profiles);
128     start_block = '{' @start_block;
129     end_block = '}' @end_block;
130     cmd_if = 'if' whitespace+ profile >strstart %start_conditional whitespace* start_block whitespace*
131              ('else' whitespace* start_block whitespace* ';' @full_conditional | ';' @half_conditional);
132     cmd = whitespace* (cmd_predicate? (cmd_install | cmd_remove | cmd_profiles) | cmd_if);
133     cmd_list = cmd* whitespace* end_block?;
134     main := cmd_list;
135 }%%
136
137 %% write data;
138
139 void badsyntax(const char *filename, int lineno, char badchar, const char *message) {
140     if (!message) {
141         if (badchar == '\n')
142             message = "Unexpected newline";
143         else if (isspace(badchar))
144             message = "Unexpected whitespace";
145         else
146             message = "Syntax error";
147     }
148
149     if (isprint(badchar) && !isspace(badchar))
150         fatal("%s: %d: %s at '%c'", filename, lineno, message, badchar);
151     else
152         fatal("%s: %d: %s", filename, lineno, message);
153 }
154
155 void parser(const char *filename, inapt_block *top_block)
156 {
157     static char buf[BUFSIZE];
158     int fd;
159     int cs, have = 0;
160     int done = 0;
161     int curline = 1;
162     char *ts = 0;
163
164     std::vector<inapt_block *> block_stack;
165     std::vector<inapt_conditional *> conditional_stack;
166     std::vector<std::string> alternates;
167     std::vector<std::string> cmd_predicates;
168     std::vector<std::string> pkg_predicates;
169     std::vector<std::string> profiles;
170     block_stack.push_back(top_block);
171     inapt_action *tmp_action = NULL;
172
173     int stack[MAXDEPTH];
174     int top = 0;
175
176     const char *curfile = filename;
177
178     if (filename) {
179         fd = open(filename, O_RDONLY);
180         if (fd < 0)
181             fatalpe("open: %s", filename);
182     } else {
183         curfile = "stdin";
184         fd = 0;
185     }
186
187     %% write init;
188
189     while (!done) {
190         char *p = buf + have, *pe, *eof = 0;
191         int len, space = BUFSIZE - have;
192
193         if (!space)
194             badsyntax(curfile, curline, 0, "Overlength token");
195
196         len = read(fd, p, space);
197         if (len < 0)
198             fatalpe("Unable to read spec");
199         pe = p + len;
200
201         if (!len) {
202             eof = pe;
203             done = 1;
204         }
205
206         %% write exec;
207
208         if (cs == inapt_error)
209             badsyntax(curfile, curline, *p, NULL);
210
211         have = 0;
212
213         if (ts) {
214             have = pe - ts;
215             memmove(buf, ts, have);
216             ts = buf;
217         }
218     }
219
220     if (cs < inapt_first_final)
221         badsyntax(curfile, curline, 0, "Unexpected EOF");
222
223     if (top)
224         badsyntax(curfile, curline, 0, "Unclosed block at EOF");
225 }