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