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