Make conditionals functional
[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
16 %%{
17     machine inapt;
18
19     action pkgstart { ts = p; }
20
21     action add_list {
22         inapt_action *tmp_action = new inapt_action;
23         tmp_action->package = xstrndup(ts, p - ts);
24         tmp_action->action = curaction;
25         tmp_action->linenum = curline;
26         tmp_action->filename = curfile;
27         block_stack.back()->actions.push_back(tmp_action);
28     }
29
30     action install {
31         curaction = inapt_action::INSTALL;
32     }
33
34     action remove {
35         curaction = inapt_action::REMOVE;
36     }
37
38     action newline {
39         curline += 1;
40     }
41
42     action start_block {
43         if (depth++ < MAXDEPTH) {
44             inapt_block *tmp_block = new inapt_block;
45             block_stack.push_back(tmp_block);
46             fcall main;
47         } else {
48             fatal("%s: %d: Syntax Error: Nesting Too Deep at '}'", curfile, curline);
49         }
50     }
51
52     action end_block {
53         if (depth--) {
54             fret;
55         } else {
56             fatal("%s: %d: Syntax Error: Unexpected '}'", curfile, curline);
57         }
58     }
59
60     action start_conditional {
61         inapt_conditional *cond = new inapt_conditional;
62         cond->condition = xstrndup(ts, p - ts);
63         conditional_stack.push_back(cond);
64     }
65
66     action full_conditional {
67         inapt_conditional *cond = conditional_stack.back(); conditional_stack.pop_back();
68         cond->else_block = block_stack.back(); block_stack.pop_back();
69         cond->then_block = block_stack.back(); block_stack.pop_back();
70         block_stack.back()->children.push_back(cond);
71     }
72
73     action half_conditional {
74         inapt_conditional *cond = conditional_stack.back(); conditional_stack.pop_back();
75         cond->else_block = NULL;
76         cond->then_block = block_stack.back(); block_stack.pop_back();
77         block_stack.back()->children.push_back(cond);
78     }
79
80     newline = '\n' @newline;
81     comment = '#' (any - '\n')* newline;
82     whitespace = [\t\v\f\r ] | comment | newline;
83     package_name = ((lower | digit) (lower | digit | '+' | '-' | '.')+) >pkgstart;
84     package_list = ((whitespace+ package_name)+ %add_list whitespace*);
85     cmd_install = ('install' @install package_list ';');
86     cmd_remove = ('remove' @remove package_list ';');
87     simple_cmd = cmd_install | cmd_remove;
88     start_block = '{' @start_block;
89     end_block = '}' @end_block;
90     cmd_if = 'if' whitespace+ alpha+ >pkgstart %start_conditional whitespace* start_block whitespace*
91              ('else' whitespace* start_block whitespace* ';' @full_conditional | ';' @half_conditional);
92     cmd_list = (simple_cmd | cmd_if | whitespace)* end_block?;
93     main := cmd_list;
94 }%%
95
96 %% write data;
97
98 #define BUFSIZE 128
99
100 void badsyntax(const char *filename, int lineno, char badchar, const char *message) {
101     if (!message) {
102         if (badchar == '\n')
103             message = "Unexpected newline";
104         else if (isspace(badchar))
105             message = "Unexpected whitespace";
106         else
107             message = "Syntax error";
108     }
109
110     if (isprint(badchar) && !isspace(badchar))
111         fatal("%s: %d: %s at '%c'", filename, lineno, message, badchar);
112     else
113         fatal("%s: %d: %s", filename, lineno, message);
114 }
115
116 void parser(const char *filename, inapt_block *top_block)
117 {
118     static char buf[BUFSIZE];
119     int fd;
120     int cs, have = 0;
121     int done = 0;
122     int curline = 1;
123     char *ts = 0, *te = 0;
124
125     std::vector<inapt_block *> block_stack;
126     std::vector<inapt_conditional *> conditional_stack;
127     block_stack.push_back(top_block);
128
129     int stack[MAXDEPTH];
130     int top = 0; /* TODO: resize */
131     int depth = 0;
132
133     const char *curfile = filename;
134     enum inapt_action::action_t curaction = inapt_action::UNSET;
135
136     if (filename) {
137         fd = open(filename, O_RDONLY);
138         if (fd < 0)
139             fatalpe("open: %s", filename);
140     } else {
141         curfile = "stdin";
142         fd = 0;
143     }
144
145     %% write init;
146
147     while ( !done ) {
148         char *p = buf + have, *pe, *eof = 0;
149         int len, space = BUFSIZE - have;
150
151         if (space == 0) {
152             fprintf(stderr, "OUT OF BUFFER SPACE\n");
153             exit(1);
154         }
155
156         len = read(fd, p, space);
157         if (len < 0) {
158             fprintf(stderr, "IO ERROR\n");
159             exit(1);
160         }
161         pe = p + len;
162
163         if (!len) {
164             eof = pe;
165             done = 1;
166         }
167
168         %% write exec;
169
170         if (cs == inapt_error)
171             badsyntax(curfile, curline, *p, NULL);
172
173         have = 0;
174
175         if (ts) {
176             have = pe - ts;
177             memmove(buf, ts, have);
178             te = buf + (te - ts);
179             ts = buf;
180         }
181     }
182
183     if (cs < inapt_first_final) {
184        fprintf(stderr, "UNEXPECTED EOF\n");
185        exit(1);
186     }
187
188     if (depth)
189         badsyntax(curfile, curline, 0, "Unclosed block at EOF");
190 }