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