r17951 MediaWiki - Code Review archive

Repository:MediaWiki
Revision:r17950‎ | r17951 | r17952 >
Date:08:32, 27 November 2006
Author:river
Status:old
Tags:
Comment:
rewrite configuration parser to use Spirit instead of lex/yacc
Modified paths:
  • /trunk/willow/src/include/confgrammar.h (added) (history)
  • /trunk/willow/src/include/expr.h (modified) (history)
  • /trunk/willow/src/include/preprocessor.h (added) (history)
  • /trunk/willow/src/willow/Makefile.in (modified) (history)
  • /trunk/willow/src/willow/confgrammar.cc (added) (history)
  • /trunk/willow/src/willow/config.cc (modified) (history)
  • /trunk/willow/src/willow/confparse.cc (modified) (history)
  • /trunk/willow/src/willow/expr.cc (modified) (history)
  • /trunk/willow/src/willow/lexer.l (deleted) (history)
  • /trunk/willow/src/willow/log.cc (modified) (history)
  • /trunk/willow/src/willow/parser.y (deleted) (history)
  • /trunk/willow/src/willow/preprocessor.cc (added) (history)

Diff [purge]

Index: trunk/willow/src/include/confgrammar.h
@@ -0,0 +1,215 @@
 2+/* Willow: Lightweight HTTP reverse-proxy. */
 3+/* confgrammar: Spirit grammar for confparse */
 4+/* Copyright (c) 2005, 2006 River Tarnell <river@attenuate.org>. */
 5+/*
 6+ * Permission is granted to anyone to use this software for any purpose,
 7+ * including commercial applications, and to alter it and redistribute it
 8+ * freely. This software is provided 'as-is', without any express or implied
 9+ * warranty.
 10+ */
 11+
 12+/* $Id$ */
 13+
 14+#ifndef CONFGRAMMAR_H
 15+#define CONFGRAMMAR_H
 16+
 17+#include <iostream>
 18+#include <vector>
 19+#include <exception>
 20+
 21+#include <boost/mpl/vector_c.hpp>
 22+#include <boost/mpl/equal.hpp>
 23+#include <boost/mpl/plus.hpp>
 24+#include <boost/mpl/minus.hpp>
 25+#include <boost/mpl/transform.hpp>
 26+#include <boost/mpl/placeholders.hpp>
 27+#include <boost/static_assert.hpp>
 28+#include <boost/variant.hpp>
 29+using std::vector;
 30+using std::ostream;
 31+using std::exception;
 32+using boost::variant;
 33+
 34+namespace mpl = boost::mpl;
 35+namespace mpp = boost::mpl::placeholders;
 36+
 37+#include "willow.h"
 38+#include "util.h"
 39+#include "preprocessor.h"
 40+#include "expr.h"
 41+
 42+struct parser_error : exception {
 43+ parser_error(char const *e) : _err(e) {}
 44+ char const *what(void) const throw() {
 45+ return _err;
 46+ }
 47+
 48+private:
 49+ char const *_err;
 50+};
 51+
 52+enum string_type {
 53+ string_tag,
 54+ quoted_string_tag
 55+};
 56+
 57+template<string_type tag>
 58+struct tagged_string {
 59+ tagged_string() {}
 60+ tagged_string(string const &s) : v(s) {}
 61+ tagged_string(tagged_string const &other) : v(other.v) {}
 62+ template<typename iter>
 63+ tagged_string(iter begin, iter end) : v(begin, end) {}
 64+ tagged_string &operator= (tagged_string const &other) {
 65+ v = other.v;
 66+ return *this;
 67+ }
 68+
 69+ string const &value(void) const {
 70+ return v;
 71+ }
 72+
 73+ string &value(void) {
 74+ return v;
 75+ }
 76+private:
 77+ string v;
 78+};
 79+
 80+typedef tagged_string<string_tag> u_string;
 81+typedef tagged_string<quoted_string_tag> q_string;
 82+
 83+typedef mpl::vector_c<int, 0, 0> scalar_d;
 84+typedef mpl::vector_c<int, 1, 0> time_d;
 85+typedef mpl::vector_c<int, 0, 1> size_d;
 86+
 87+template<typename T, typename D>
 88+struct quantity {
 89+ quantity() : _value(0) {}
 90+ explicit quantity(T v) : _value(v) {}
 91+ explicit quantity(quantity<T, scalar_d> const &o)
 92+ : _value(o.value()) {}
 93+
 94+ template<typename D2>
 95+ quantity(quantity<T, D2> const &other)
 96+ : _value(other.value()) {
 97+ BOOST_STATIC_ASSERT((
 98+ mpl::equal<D, D2>::type::value
 99+ ));
 100+ }
 101+
 102+ quantity<T, D> &operator= (quantity<T, D> const &o) {
 103+ _value = o.value();
 104+ return *this;
 105+ }
 106+
 107+ template<typename D2>
 108+ quantity<T, D> &operator= (quantity<T, D> const &o) {
 109+ BOOST_STATIC_ASSERT((
 110+ mpl::equal<D, D2>::type::value
 111+ ));
 112+ _value = o.value();
 113+ return *this;
 114+ }
 115+
 116+ quantity<T, D> &operator+= (quantity<T, D> const &o) {
 117+ _value += o._value;
 118+ return *this;
 119+ }
 120+
 121+ quantity<T, D> &operator-= (quantity<T, D> const &o) {
 122+ _value -= o._value;
 123+ return *this;
 124+ }
 125+
 126+ template<typename D2>
 127+ quantity<T, D> &operator*= (quantity<T, D2> const &o) {
 128+ *this = *this * o;
 129+ return *this;
 130+ }
 131+
 132+ template<typename D2>
 133+ quantity<T, D> &operator/= (quantity<T, D2> const &o) {
 134+ *this = *this / o;
 135+ return *this;
 136+ }
 137+
 138+ T value(void) const { return _value; }
 139+private:
 140+ T _value;
 141+};
 142+
 143+template<typename T, typename D>
 144+quantity<T, D>
 145+operator+ (quantity<T, D> a, quantity<T, D> b)
 146+{
 147+ return quantity<T, D>(a.value() + b.value());
 148+}
 149+
 150+template<typename T, typename D>
 151+quantity<T, D>
 152+operator- (quantity<T, D> a, quantity<T, D> b)
 153+{
 154+ return quantity<T, D>(a.value() + b.value());
 155+}
 156+
 157+template<typename T, typename D1, typename D2>
 158+quantity<T, typename mpl::transform<D1, D2, mpl::plus<mpp::_1, mpp::_2> >::type>
 159+operator* (quantity<T, D1> a, quantity<T, D2> b)
 160+{
 161+ return quantity<T, typename mpl::transform<D1, D2,
 162+ mpl::plus<mpp::_1, mpp::_2> >::type
 163+ >(a.value() * b.value());
 164+}
 165+
 166+template<typename T, typename D1, typename D2>
 167+quantity<T, typename mpl::transform<D1, D2, mpl::minus<mpp::_1, mpp::_2> >::type>
 168+operator/ (quantity<T, D1> a, quantity<T, D2> b)
 169+{
 170+ return quantity<T, typename mpl::transform<D1, D2,
 171+ mpl::minus<mpp::_1, mpp::_2> >::type
 172+ >(a.value() / b.value());
 173+}
 174+
 175+template<typename T, typename D>
 176+ostream&
 177+operator<< (ostream &o, quantity<T, D> q)
 178+{
 179+ o << q.value();
 180+ return o;
 181+}
 182+
 183+typedef quantity<int, scalar_d> scalar_q;
 184+typedef quantity<int, time_d> time_q;
 185+typedef quantity<int, size_d> size_q;
 186+
 187+typedef variant<u_string, q_string, bool, int, time_q, size_q> avalue_t;
 188+
 189+struct value_t {
 190+ string name;
 191+ vector<avalue_t> values;
 192+
 193+ value_t(string const &name_, vector<avalue_t> const &vals_)
 194+ : name(name_), values(vals_) {}
 195+};
 196+
 197+struct block {
 198+ block();
 199+ block(string const &name_, string const &value_, vector<value_t> const &values_)
 200+ : name(name_), key(value_), values(values_) {}
 201+
 202+ string name;
 203+ string key;
 204+
 205+ vector<value_t> values;
 206+};
 207+
 208+struct confgrammar {
 209+ confgrammar(expr::parser const &expr);
 210+ vector<block> parse(string const &file);
 211+
 212+private:
 213+ expr::parser const &_expr;
 214+};
 215+
 216+#endif /* CONFGRAMMAR_H */
Property changes on: trunk/willow/src/include/confgrammar.h
___________________________________________________________________
Added: svn:keywords
1217 + Id Revision
Index: trunk/willow/src/include/expr.h
@@ -15,16 +15,16 @@
1616
1717 namespace expr {
1818
19 -struct expression_error : runtime_error {
20 - expression_error(char const *err) : runtime_error(err) {}
 19+struct error : runtime_error {
 20+ error(char const *err) : runtime_error(err) {}
2121 };
2222
23 -struct stack_underflow : expression_error {
24 - stack_underflow() : expression_error("stack underflow in expression parser") {}
 23+struct stack_underflow : error {
 24+ stack_underflow() : error("stack underflow in expression parser") {}
2525 };
2626
27 -struct syntax_error : expression_error {
28 - syntax_error() : expression_error("syntax error in expression parser") {}
 27+struct syntax_error : error {
 28+ syntax_error() : error("syntax error in expression parser") {}
2929 };
3030
3131 struct expression_parser_impl;
@@ -52,16 +52,16 @@
5353 * test whether a variable exists: defined(x) has the value '1' if 'x' is
5454 * defined.
5555 */
56 -struct expression_parser {
 56+struct parser {
5757 /**
5858 * Construct a new parser.
5959 */
60 - expression_parser();
 60+ parser();
6161
6262 /**
6363 * Destructor.
6464 */
65 - ~expression_parser();
 65+ ~parser();
6666
6767 /**
6868 * Add a new variable to this parser.
Index: trunk/willow/src/include/preprocessor.h
@@ -0,0 +1,127 @@
 2+/* Willow: Lightweight HTTP reverse-proxy. */
 3+/* confgrammar: Spirit grammar for confparse */
 4+/* Copyright (c) 2005, 2006 River Tarnell <river@attenuate.org>. */
 5+/*
 6+ * Permission is granted to anyone to use this software for any purpose,
 7+ * including commercial applications, and to alter it and redistribute it
 8+ * freely. This software is provided 'as-is', without any express or implied
 9+ * warranty.
 10+ */
 11+
 12+/* $Id$ */
 13+
 14+#ifndef PREPROCESSOR_H
 15+#define PREPROCESSOR_H
 16+
 17+#include <iterator>
 18+#include <iostream>
 19+#include <exception>
 20+using std::exception;
 21+using std::ostream;
 22+using std::random_access_iterator_tag;
 23+
 24+#include "willow.h"
 25+#include "expr.h"
 26+
 27+struct preprocessor_exception : exception {
 28+ preprocessor_exception(char const *err) : _err(err) {}
 29+ char const *what(void) const throw() {
 30+ return _err;
 31+ }
 32+private:
 33+ char const *_err;
 34+};
 35+
 36+struct file_position {
 37+ file_position(string const &f, int l, int p);
 38+ file_position(file_position const &o);
 39+ file_position &operator= (file_position const &o);
 40+
 41+ string file;
 42+ int line;
 43+ int col;
 44+};
 45+
 46+ostream& operator<< (ostream &o, file_position const &p);
 47+
 48+struct preprocessor {
 49+ struct iterator {
 50+ typedef char value_type;
 51+ typedef value_type const &reference;
 52+ typedef reference const_reference;
 53+ typedef value_type const *pointer;
 54+ typedef vector<char>::difference_type difference_type;
 55+ typedef vector<char>::size_type size_type;
 56+ typedef random_access_iterator_tag iterator_category;
 57+
 58+ preprocessor const *_cpp;
 59+ vector<char>::const_iterator _it;
 60+
 61+ iterator();
 62+ iterator(preprocessor const &cpp, vector<char>::const_iterator it);
 63+ iterator(iterator const &other);
 64+
 65+ iterator& operator= (iterator const &other);
 66+ iterator& operator++(void);
 67+ iterator operator++(int);
 68+ iterator& operator--(void);
 69+ iterator operator--(int);
 70+ reference operator*(void) const;
 71+
 72+ bool operator< (iterator const &other) const;
 73+ bool operator== (iterator const &other) const;
 74+ bool operator!= (iterator const &other) const;
 75+ bool operator> (iterator const &other) const;
 76+
 77+ bool operator<= (iterator const &other) const;
 78+ bool operator>= (iterator const &other) const;
 79+ difference_type operator- (iterator const &other) const;
 80+ iterator operator- (size_type s) const;
 81+ iterator operator+ (size_type s) const;
 82+ file_position get_position(void) const;
 83+ string const &get_line(int i) const;
 84+ };
 85+
 86+ typedef iterator const_iterator;
 87+ typedef char value_type;
 88+ typedef value_type const &reference;
 89+ typedef value_type const &const_reference;
 90+ typedef vector<char>::size_type size_type;
 91+ typedef vector<char>::difference_type difference_type;
 92+
 93+ map<vector<char>::difference_type, file_position> positions;
 94+ vector<string> lines;
 95+
 96+ void set_position(vector<char>::const_iterator n);
 97+
 98+ string const &get_line(int line) const;
 99+ file_position get_position(vector<char>::const_iterator it) const;
 100+
 101+ preprocessor(string const &file, expr::parser const &expr);
 102+
 103+ iterator begin(void);
 104+ const_iterator begin(void) const;
 105+ iterator end(void);
 106+ const_iterator end(void) const;
 107+
 108+private:
 109+ template<typename iterT>
 110+ void process(iterT begin, iterT end);
 111+
 112+ template<typename iterT>
 113+ bool evaluate_expr(iterT begin, iterT end, bool ignoring);
 114+
 115+ template<typename iterT>
 116+ bool itercmp(iterT begin, iterT end, char const *s);
 117+
 118+ vector<char> _data;
 119+ string _curfile;
 120+ int _curline;
 121+ int _curpos;
 122+ iterator _end;
 123+ expr::parser const &
 124+ _expr;
 125+};
 126+
 127+
 128+#endif /* PREPROCESSOR_H */
Property changes on: trunk/willow/src/include/preprocessor.h
___________________________________________________________________
Added: svn:keywords
1129 + Id Revision
Index: trunk/willow/src/willow/parser.y
@@ -1,558 +0,0 @@
2 -/* Willow: Lightweight HTTP reverse-proxy. */
3 -/* Copyright (c) 2005, 2006 River Tarnell <river@attenuate.org>. */
4 -/*
5 - * Permission is granted to anyone to use this software for any purpose,
6 - * including commercial applications, and to alter it and redistribute it
7 - * freely. This software is provided 'as-is', without any express or implied
8 - * warranty.
9 - */
10 -
11 -/* From: $Nightmare: nightmare/src/main/parser.y,v 1.2.2.1.2.1 2002/07/02 03:42:10 ejb Exp $ */
12 -/* $Id$ */
13 -
14 -%{
15 -#include <sys/types.h>
16 -#include <sys/stat.h>
17 -
18 -#include <netinet/in.h>
19 -#include <arpa/inet.h>
20 -#include <netdb.h>
21 -
22 -#include <vector>
23 -#include <cstdlib>
24 -#include <cstdarg>
25 -#include <cstdio>
26 -using std::vector;
27 -using std::sprintf;
28 -
29 -#define NEED_PARSING_TREE
30 -#include "willow.h"
31 -#include "confparse.h"
32 -
33 -/*
34 - * Certain bison/gcc combinations result in compilation errors in gcc-specific
35 - * code. Work around this by undefining here.
36 - */
37 -#undef __GNUC_MINOR__
38 -
39 -#define YY_NO_UNPUT
40 -
41 -/* icc emits these with -w2 */
42 -#ifdef __INTEL_COMPILER
43 -# pragma warning (disable : 193)
44 -#endif
45 -
46 -static time_t conf_find_time(string const &);
47 -
48 -static struct {
49 - const char * name;
50 - const char * plural;
51 - time_t val;
52 -} conf_times[] = {
53 - {"second", "seconds", 1},
54 - {"minute", "minutes", 60},
55 - {"hour", "hours", 60 * 60},
56 - {"day", "days", 60 * 60 * 24},
57 - {"week", "weeks", 60 * 60 * 24 * 7},
58 - {"fortnight", "fortnights", 60 * 60 * 24 * 14},
59 - {"month", "months", 60 * 60 * 24 * 7 * 4},
60 - {"year", "years", 60 * 60 * 24 * 365},
61 - /* ok-- we now do sizes here too. they aren't times, but
62 - it's close enough */
63 - {"byte", "bytes", 1},
64 - {"kb", NULL, 1024},
65 - {"kbyte", "kbytes", 1024},
66 - {"kilobyte", "kilebytes", 1024},
67 - {"mb", NULL, 1024 * 1024},
68 - {"mbyte", "mbytes", 1024 * 1024},
69 - {"megabyte", "megabytes", 1024 * 1024},
70 - {NULL, NULL, 0},
71 -};
72 -
73 -time_t conf_find_time(string const &name)
74 -{
75 - int i;
76 -
77 - for (i = 0; conf_times[i].name; i++)
78 - {
79 - if (!strcasecmp(conf_times[i].name, name.c_str()) ||
80 - (conf_times[i].plural && !strcasecmp(conf_times[i].plural, name.c_str())))
81 - return conf_times[i].val;
82 - }
83 -
84 - return 0;
85 -}
86 -
87 -/*ARGSUSED*/
88 -static conf::value *
89 -f_hostname(vector<conf::avalue> *)
90 -{
91 -#ifndef HOST_NAME_MAX
92 -# define HOST_NAME_MAX 255 /* SUSv2 */
93 -#endif
94 -char host[HOST_NAME_MAX] = { 0 };
95 -conf::value *ret;
96 -conf::avalue aval;
97 - gethostname(host, sizeof(host));
98 - ret = new conf::value(conf::declpos::here());
99 - aval.av_type = conf::cv_qstring;
100 - aval.av_strval = host;
101 - ret->cv_values.push_back(aval);
102 - return ret;
103 -}
104 -
105 -static conf::value *
106 -f_dns(vector<conf::avalue> *args)
107 -{
108 -struct addrinfo *res, hints;
109 -string aftype;
110 -conf::value *ret;
111 -int i;
112 -conf::avalue aval;
113 -char tmp[64];
114 - ret = new conf::value(conf::declpos::here());
115 - aftype = (*args)[0].av_strval.c_str();
116 - memset(&hints, 0, sizeof(hints));
117 - if (aftype == "ipv4")
118 - hints.ai_family = AF_INET;
119 - else if (aftype == "ipv6")
120 - hints.ai_family = AF_INET6;
121 - else {
122 - return ret;
123 - }
124 - if ((i = getaddrinfo((*args)[0].av_strval.c_str(), "80", &hints, &res)) != 0) {
125 - conf::report_parse_error("getaddrinfo(%s): %s",
126 - (*args)[0].av_strval.c_str(), gai_strerror(i));
127 - return ret;
128 - }
129 -
130 - /* format the address as an IP */
131 - aval.av_type = conf::cv_qstring;
132 - inet_ntop(res->ai_family, res->ai_addr->sa_data, tmp, sizeof(tmp));
133 - aval.av_strval = tmp;
134 - ret->cv_values.push_back(aval);
135 - freeaddrinfo(res);
136 - return ret;
137 -}
138 -
139 -typedef struct function_stru {
140 - const char *name;
141 - conf::value *(*execute)(vector<conf::avalue> *args);
142 - int args[3]; /* XXX */
143 -} function_t;
144 -
145 -static function_t functions[] = {
146 - { "hostname", f_hostname, { 0, 0, 0 } },
147 - { "dns", f_dns, { conf::cv_qstring, conf::cv_string, 0 } },
148 - { NULL, NULL, { } }
149 -};
150 -
151 -static int
152 -match_func_parms(function_t *f, vector<conf::avalue> *args)
153 -{
154 -size_t i;
155 -vector<conf::avalue>::const_iterator it, end;
156 - it = args->begin();
157 - end = args->end();
158 - for (i = 0; f->args[i]; ++i) {
159 - if (i+1 > args->size()) {
160 - conf::report_parse_error("not enough arguments to function '%s' (got %d)",
161 - f->name, i+1);
162 - return 0;
163 - }
164 - if (f->args[i] != (it)->av_type) {
165 - conf::report_parse_error("wrong type %d for argument %d to '%s' (expected %d)",
166 - (it)->av_type, i + 1, f->name, f->args[i]);
167 - return 0;
168 - }
169 - it++;
170 - }
171 - if (args->size() > i) {
172 - conf::report_parse_error("too many arguments to function '%s'", f->name);
173 - return 0;
174 - }
175 - return 1;
176 -}
177 -
178 -static function_t*
179 -find_function(string const &name, vector<conf::avalue> *args)
180 -{
181 -function_t *f;
182 - for (f = functions; f->name; ++f)
183 - if (f->name == name)
184 - {
185 - if (match_func_parms(f, args))
186 - return f;
187 - else
188 - break;
189 - }
190 - return NULL;
191 -}
192 -
193 -static struct
194 -{
195 - const char *word;
196 - int yesno;
197 -} yesno[] = {
198 - {"yes", 1},
199 - {"no", 0},
200 - {"true", 1},
201 - {"false", 0},
202 - {"on", 1},
203 - {"off", 0},
204 - {NULL, 0}
205 -};
206 -
207 -static int
208 -conf_get_yesno_value(string const &str)
209 -{
210 -int i;
211 - for (i = 0; yesno[i].word; i++)
212 - if (str == yesno[i].word)
213 - return yesno[i].yesno;
214 -
215 - return -1;
216 -}
217 -
218 -%}
219 -
220 -%union {
221 - long number;
222 - string *string_;
223 - conf::avalue *avalue;
224 - conf::value *value;
225 - vector<conf::value> *value_list;
226 - vector<conf::avalue> *avalue_list;
227 - bool bool_;
228 -}
229 -
230 -%token TWODOTS VAR TEMPLATE FROM
231 -
232 -%token <string_> QSTRING STRING VARNAME
233 -%token <number> NUMBER
234 -
235 -%type <string_> qstring string varname astring from_clause key_clause
236 -%type <number> number timespec
237 -%type <avalue> oneitem
238 -%type <avalue_list> single
239 -%type <avalue_list> itemlist
240 -%type <value_list> block_items optional_block
241 -%type <value> block_item
242 -%type <bool_> template_clause
243 -%type <avalue_list> func_args
244 -%type <value> function
245 -
246 -%left '+'
247 -%nonassoc poneitem
248 -%nonassoc ptimespec
249 -%start conf
250 -
251 -%%
252 -
253 -conf: | items
254 -
255 -items: conf_item
256 - | items conf_item
257 - ;
258 -
259 -conf_item: block
260 - ;
261 -
262 -from_clause:
263 - { $$ = NULL; }
264 - | FROM astring {
265 - $$ = $2;
266 - }
267 - ;
268 -key_clause:
269 - { $$ = NULL; }
270 - | astring {
271 - $$ = $1;
272 - }
273 - ;
274 -
275 -template_clause:
276 - { $$ = 0; }
277 - | TEMPLATE { $$ = 1; }
278 - ;
279 -
280 -semicolon:
281 - ';'
282 - | { conf::report_parse_error("expected ';'"); }
283 - ;
284 -
285 -equals:
286 - '='
287 - | { conf::report_parse_error("expected '='"); }
288 - ;
289 -
290 -func_args:
291 - { $$ = new vector<conf::avalue>; }
292 - | itemlist {
293 - $$ = $1;
294 - delete $1;
295 - }
296 - ;
297 -
298 -function:
299 - string '(' func_args ')'
300 - {
301 - function_t *func;
302 - if ((func = find_function(*$1, $3)) == NULL) {
303 - conf::report_parse_error("undefined function %s", $1);
304 - $$ = new conf::value(conf::declpos::here());
305 - } else {
306 - $$ = func->execute($3);
307 - }
308 - delete $1;
309 - }
310 - ;
311 -
312 -optional_block:
313 - {
314 - $$ = NULL;
315 - }
316 - | '{' block_items '}'
317 - {
318 - $$ = $2;
319 - }
320 - ;
321 -
322 -block: template_clause string key_clause from_clause optional_block semicolon
323 - {
324 - const char *block_key;
325 - char nname[10];
326 - static int nseq;
327 - bool unnamed = false;
328 - conf::tree_entry *e;
329 - vector<conf::value>::const_iterator it, end;
330 - if ($3)
331 - block_key = $3->c_str();
332 - else {
333 - sprintf(nname, "__%d", nseq++);
334 - block_key = nname;
335 - unnamed = true;
336 - }
337 - if ($4) {
338 - if ((e = conf::new_tree_entry_from_template(conf::parsing_tree, *$2, block_key, *$4,
339 - conf::declpos::here(), unnamed, $1)) == NULL) {
340 - conf::report_parse_error("template block \"%s\" not found", $4);
341 - goto end;
342 - }
343 - } else {
344 -#if 0
345 - if ((e = conf::parsing_tree.find(*$2, block_key)) != NULL) {
346 - conf::report_parse_error("%s \"%s\" already defined at %s",
347 - $2->c_str(), block_key, e->item_pos.format().c_str());
348 - goto end;
349 - }
350 -#endif
351 - e = conf::parsing_tree.create(*$2, block_key, conf::declpos::here(), unnamed, $1);
352 - }
353 -
354 - if ($5) for (it = $5->begin(), end = $5->end(); it != end; ++it) {
355 - e->add(*it);
356 - }
357 - end:
358 - delete $2;
359 - delete $3;
360 - delete $4;
361 - delete $5;
362 - }
363 - | VAR varname equals itemlist semicolon
364 - {
365 - conf::value *value;
366 - value = new conf::value(conf::declpos::here());
367 - value->cv_name = $2->substr(1);
368 - value->cv_values = *$4;
369 - conf::add_variable(value);
370 - delete $4;
371 - }
372 - | error
373 - ;
374 -
375 -block_items:
376 - block_items block_item
377 - {
378 - $$ = new vector<conf::value>($1->begin(), $1->end());
379 - $$->push_back(*$2);
380 - delete $1;
381 - delete $2;
382 - }
383 - | block_item
384 - {
385 - $$ = new vector<conf::value>;
386 - $$->push_back(*$1);
387 - delete $1;
388 - }
389 - ;
390 -
391 -block_item:
392 - string equals itemlist semicolon
393 - {
394 - $$ = new conf::value(conf::declpos::here());
395 - $$->cv_name = *$1;
396 - $$->cv_values = *$3;
397 - delete $1;
398 - delete $3;
399 - }
400 - ;
401 -
402 -/*
403 - * "single" is a list of items.
404 - */
405 -itemlist: itemlist ',' single
406 - {
407 - $$ = $1;
408 - $$->insert($$->end(), $3->begin(), $3->end());
409 - delete $3;
410 - }
411 - | single
412 - {
413 - $$ = new vector<conf::avalue>;
414 - $$->insert($$->end(), $1->begin(), $1->end());
415 - delete $1;
416 - }
417 - ;
418 -
419 -single: oneitem
420 - {
421 - $$ = new vector<conf::avalue>;
422 - $$->push_back(*$1);
423 - delete $1;
424 - }
425 - | oneitem TWODOTS oneitem
426 - {
427 - $$ = new vector<conf::avalue>;
428 - /* "1 .. 5" meaning 1,2,3,4,5 - only valid for integers */
429 - if ($1->av_type != conf::cv_int || $3->av_type != conf::cv_int) {
430 - conf::report_parse_error("both arguments in '..' notation must be integers.");
431 - break;
432 - } else {
433 - int i;
434 - conf::avalue val;
435 - for (i = $1->av_intval; i <= $3->av_intval; i++) {
436 - val.av_type = conf::cv_int;
437 - val.av_intval = i;
438 - $$->push_back(val);
439 - }
440 - }
441 - delete $1;
442 - delete $3;
443 - }
444 - | varname
445 - {
446 - string varname_;
447 - conf::value *value;
448 - $$ = new vector<conf::avalue>;
449 - varname_ = $1->substr(1);
450 - value = conf::value_from_variable("", varname_, conf::declpos::here());
451 - if (value == NULL) {
452 - conf::report_parse_error("undefined variable %s", varname_.c_str());
453 - } else {
454 - $$->insert($$->begin(), value->cv_values.begin(), value->cv_values.end());
455 - }
456 - delete $1;
457 - }
458 - | single '+' single
459 - {
460 - int flen, slen;
461 - flen = $1->size();
462 - slen = $3->size();
463 - if (flen == 1 && slen == 1) {
464 - conf::avalue n;
465 - n.av_type = conf::cv_qstring;
466 - n.av_strval = (*$1)[0].av_strval + (*$3)[0].av_strval;
467 - $$->push_back(n);
468 - } else {
469 - conf::report_parse_error("do not know how to add these values (%d+%d)", flen, slen);
470 - }
471 - delete $1;
472 - delete $3;
473 - }
474 - ;
475 -
476 -oneitem: astring
477 - {
478 - $$ = new conf::avalue;
479 - $$->av_type = conf::cv_qstring;
480 - $$->av_strval = *$1;
481 - delete $1;
482 - }
483 - | timespec
484 - {
485 - $$ = new conf::avalue;
486 - $$->av_type = conf::cv_time;
487 - $$->av_intval = $1;
488 - }
489 - | number
490 - {
491 - $$ = new conf::avalue;
492 - $$->av_type = conf::cv_int;
493 - $$->av_intval = $1;
494 - }
495 - | string
496 - {
497 - /* a 'string' could also be a yes/no value ..
498 - so pass it as that, if so */
499 - int val = conf_get_yesno_value(*$1);
500 -
501 - $$ = new conf::avalue;
502 -
503 - if (val != -1) {
504 - $$->av_type = conf::cv_yesno;
505 - $$->av_intval = val;
506 - } else {
507 - $$->av_type = conf::cv_string;
508 - $$->av_strval = *$1;
509 - }
510 - delete $1;
511 - }
512 - ;
513 -
514 -astring:
515 - qstring {
516 - $$ = $1;
517 - }
518 - | function {
519 - conf::value *value;
520 - value = $1;
521 - if (!value->is_single(conf::cv_qstring)) {
522 - value->report_error("function in concatenation must return quoted string");
523 - $$ = new string("");
524 - } else {
525 - $$ = new string($1->cv_values[0].av_strval);
526 - }
527 - delete $1;
528 - }
529 - ;
530 -
531 -qstring:
532 - QSTRING {
533 - $$ = $1;
534 - }
535 - | QSTRING QSTRING {
536 - $$ = new string (*$1 + *$2);
537 - delete $1;
538 - delete $2;
539 - }
540 - ;
541 -string: STRING { $$ = $1; } ;
542 -number: NUMBER { $$ = $1; } ;
543 -varname: VARNAME { $$ = $1; } ;
544 -
545 -timespec: number string
546 - {
547 - time_t t;
548 - if ((t = conf_find_time(*$2)) == 0) {
549 - conf::report_parse_error("unrecognised time type/size \"%s\"", $2->c_str());
550 - t = 1;
551 - }
552 - delete $2;
553 - $$ = $1 * t;
554 - }
555 - | timespec timespec
556 - {
557 - $$ = $1 + $2;
558 - }
559 - ;
Index: trunk/willow/src/willow/lexer.l
@@ -1,177 +0,0 @@
2 -/* Willow: Lightweight HTTP reverse-proxy. */
3 -/* lexer: config lexer */
4 -/* Copyright (c) 2005, 2006 River Tarnell <river@attenuate.org>. */
5 -/*
6 - * Permission is granted to anyone to use this software for any purpose,
7 - * including commercial applications, and to alter it and redistribute it
8 - * freely. This software is provided 'as-is', without any express or implied
9 - * warranty.
10 - */
11 -
12 -/* $Id$ */
13 -
14 -%option noyywrap
15 -%option nounput
16 -
17 -%{
18 -#include <iostream>
19 -#include <string>
20 -#include <cerrno>
21 -#include <cstdio>
22 -
23 -using std::strerror;
24 -using std::fopen;
25 -
26 -#include "confparse.h"
27 -#include "y.tab.h"
28 -#include "willow.h"
29 -
30 -struct func_attrs;
31 -
32 -int yylex(void);
33 -
34 -#define MAXINCLUDE 10
35 -struct statestack_stru {
36 - YY_BUFFER_STATE state;
37 - string file;
38 - int lineno;
39 -} statestack[MAXINCLUDE + 1];
40 -int inclevel;
41 -static bool in_if;
42 -
43 -#undef YY_INPUT
44 -
45 -#define YY_FATAL_ERROR(msg) conf::catastrophic_error(msg)
46 -
47 -%}
48 -
49 -ws [ \t]*
50 -space [ \t]{ws}
51 -digit [0-9]
52 -number {digit}{digit}*
53 -qstring \"[^\"\n]*[\"\n]
54 -string [a-zA-Z_][a-zA-Z0-9_-]*
55 -include %{ws}include{space}(\<.*\>|\".*\")
56 -pragma %{ws}pragma{space}.*$
57 -if %{ws}if{space}.*$
58 -else %{ws}else{ws}$
59 -endif %{ws}endif{ws}$
60 -define %{ws}define{space}{string}{space}.*$
61 -ifdef %{ws}ifdef{space}{string}{ws}$
62 -ifndef %{ws}ifndef{space}{string}{ws}$
63 -
64 -%x COMMENT IGNORING
65 -%%
66 -
67 -^.* {
68 - conf::linebuf.assign(yytext, yyleng);
69 - conf::curpos = 0;
70 - yyless(0);
71 - }
72 -<IGNORING>{if} { conf::report_parse_error("%%if may not be nested"); }
73 -<IGNORING>{endif} { BEGIN 0; }
74 -<IGNORING>{else} { in_if = true; BEGIN 0; }
75 -<IGNORING>. ;
76 -<IGNORING>\n { conf::lineno++; }
77 -
78 -"/*" { BEGIN COMMENT; }
79 -<COMMENT>\*\/ { BEGIN 0; }
80 -<COMMENT>. ;
81 -<COMMENT>\n { conf::lineno++; conf::curpos = 0; }
82 -\/\/.*$ ;
83 -#.*$ ;
84 -\n conf::lineno++;
85 -{pragma} { conf::handle_pragma(yytext); }
86 -{if} { if (!conf::if_true(yytext))
87 - BEGIN IGNORING;
88 - else
89 - in_if = true;
90 - }
91 -{define} { conf::define_if(yytext + 8); }
92 -{ifdef} { if (conf::if_defined(yytext + 6))
93 - in_if = true;
94 - else
95 - BEGIN IGNORING;
96 - }
97 -{ifndef} { if (conf::if_defined(yytext + 7))
98 - BEGIN IGNORING;
99 - else
100 - in_if = true;
101 - }
102 -{else} { if (in_if)
103 - BEGIN IGNORING;
104 - else
105 - conf::report_parse_error("%%else without %%if");
106 - }
107 -{endif} { if (in_if)
108 - in_if = false;
109 - else
110 - conf::report_parse_error("%%endif without %%if");
111 - }
112 -{include} {
113 - FILE *f;
114 - char *s, *t, c;
115 - string file, path;
116 - s = yytext + yyleng - 1;
117 - c = *s;
118 - t = s - 1;
119 - while (*t != c)
120 - t--;
121 - file.assign(t + 1, s);
122 - if (conf::find_include(file) == false) {
123 - conf::catastrophic_error("cannot locate include file");
124 - } else if ((f = fopen(file.c_str(), "r")) == NULL) {
125 - string error = "cannot open include: ";
126 - error += file; error += " ";
127 - error += strerror(errno);
128 - conf::catastrophic_error(error.c_str());
129 - } else {
130 - if (inclevel + 1 == MAXINCLUDE) {
131 - conf::catastrophic_error("maximum include depth reached");
132 - } else {
133 - statestack[inclevel].state = YY_CURRENT_BUFFER;
134 - statestack[inclevel].lineno = conf::lineno;
135 - statestack[inclevel].file = conf::current_file;
136 - inclevel++;
137 - statestack[inclevel].state = yy_create_buffer(f, YY_BUF_SIZE);
138 - conf::current_file = file;
139 - conf::lineno = 1;
140 - yy_switch_to_buffer(statestack[inclevel].state);
141 - }
142 - }
143 - }
144 -<<EOF>> {
145 - if (inclevel) {
146 - yy_delete_buffer(statestack[inclevel].state);
147 - yy_switch_to_buffer(statestack[inclevel - 1].state);
148 - inclevel--;
149 - conf::current_file = statestack[inclevel].file;
150 - conf::lineno = statestack[inclevel].lineno;
151 - } else
152 - yyterminate();
153 - }
154 -
155 -\t { conf::curpos += 8; }
156 -{ws} { conf::curpos += yyleng; }
157 -var { conf::curpos += yyleng; return VAR; }
158 -template { conf::curpos += yyleng; return TEMPLATE; }
159 -from { conf::curpos += yyleng; return FROM; }
160 -\.\. { conf::curpos += yyleng; return TWODOTS; }
161 -
162 -{qstring} {
163 - yylval.string_ = new string(yytext + 1, yyleng - 2);
164 - conf::curpos += yyleng;
165 - return QSTRING;
166 - }
167 -{string} {
168 - yylval.string_ = new string(yytext, yyleng);
169 - conf::curpos += yyleng;
170 - return STRING;
171 - }
172 -{number} {
173 - yylval.number = strtol(yytext, NULL, 0);
174 - conf::curpos += yyleng;
175 - return NUMBER;
176 - }
177 -. { conf::curpos++; return yytext[0]; }
178 -%%
Index: trunk/willow/src/willow/confgrammar.cc
@@ -0,0 +1,361 @@
 2+/* Willow: Lightweight HTTP reverse-proxy. */
 3+/* confgrammar: Spirit grammar for confparse */
 4+/* Copyright (c) 2005, 2006 River Tarnell <river@attenuate.org>. */
 5+/*
 6+ * Permission is granted to anyone to use this software for any purpose,
 7+ * including commercial applications, and to alter it and redistribute it
 8+ * freely. This software is provided 'as-is', without any express or implied
 9+ * warranty.
 10+ */
 11+
 12+/* $Id$ */
 13+
 14+#include <iostream>
 15+#include <iterator>
 16+#include <string>
 17+#include <map>
 18+#include <exception>
 19+
 20+#include <boost/spirit.hpp>
 21+#include <boost/spirit/attribute.hpp>
 22+#include <boost/spirit/phoenix.hpp>
 23+#include <boost/spirit/phoenix/casts.hpp>
 24+#include <boost/spirit/phoenix/closures.hpp>
 25+
 26+#include <boost/variant.hpp>
 27+#include <boost/static_assert.hpp>
 28+
 29+#include <boost/mpl/vector_c.hpp>
 30+#include <boost/mpl/equal.hpp>
 31+#include <boost/mpl/plus.hpp>
 32+#include <boost/mpl/minus.hpp>
 33+#include <boost/mpl/transform.hpp>
 34+#include <boost/mpl/placeholders.hpp>
 35+
 36+#include "util.h"
 37+#include "willow.h"
 38+#include "confgrammar.h"
 39+#include "preprocessor.h"
 40+
 41+using std::random_access_iterator_tag;
 42+using std::pair;
 43+using std::make_pair;
 44+
 45+namespace mpl = boost::mpl;
 46+namespace mpp = boost::mpl::placeholders;
 47+namespace spirit = boost::spirit;
 48+using boost::variant;
 49+using spirit::grammar;
 50+using spirit::scanner;
 51+using spirit::chset;
 52+using spirit::c_escape_ch_p;
 53+using spirit::int_p;
 54+using spirit::rule;
 55+using spirit::ch_p;
 56+using spirit::confix_p;
 57+using spirit::file_iterator;
 58+using spirit::parse_info;
 59+using spirit::comment_p;
 60+using spirit::str_p;
 61+using spirit::assertion;
 62+using spirit::guard;
 63+using spirit::error_status;
 64+using spirit::lexeme_d;
 65+using spirit::as_lower_d;
 66+using spirit::symbols;
 67+using phoenix::var;
 68+using phoenix::arg1;
 69+using phoenix::arg2;
 70+using phoenix::construct_;
 71+
 72+struct block_closure : spirit::closure<block_closure,
 73+ string, string, vector<value_t> > {
 74+ member1 name;
 75+ member2 key;
 76+ member3 values;
 77+};
 78+
 79+struct file_closure : spirit::closure<file_closure, vector<block> > {
 80+ member1 blocks;
 81+};
 82+
 83+struct value_closure : spirit::closure<
 84+ value_closure,
 85+ string,
 86+ vector<avalue_t>
 87+ > {
 88+ member1 name;
 89+ member2 values;
 90+};
 91+
 92+struct avalue_closure : spirit::closure<
 93+ avalue_closure,
 94+ avalue_t
 95+ > {
 96+ member1 value;
 97+};
 98+
 99+struct expr_closure : spirit::closure<expr_closure, int> {
 100+ member1 val;
 101+};
 102+
 103+struct size_closure : spirit::closure<size_closure, size_q, scalar_q> {
 104+ member1 value;
 105+ member2 multiplier;
 106+};
 107+
 108+struct time_closure : spirit::closure<time_closure, time_q, scalar_q> {
 109+ member1 value;
 110+ member2 multiplier;
 111+};
 112+
 113+struct sizev_closure : spirit::closure<sizev_closure, size_q> {
 114+ member1 val;
 115+};
 116+
 117+struct timev_closure : spirit::closure<timev_closure, time_q> {
 118+ member1 val;
 119+};
 120+
 121+enum errors {
 122+ semicolon_expected,
 123+ equals_expected,
 124+ close_brace_expected,
 125+ open_brace_expected
 126+};
 127+
 128+struct error_handler {
 129+ template<typename ScannerT, typename ErrorT>
 130+ error_status<> operator()(ScannerT const &scan, ErrorT const &err) const {
 131+ file_position pos = err.where.get_position();
 132+ string errtxt;
 133+
 134+ switch (err.descriptor) {
 135+ case semicolon_expected:
 136+ errtxt = "expected semicolon";
 137+ break;
 138+ case equals_expected:
 139+ errtxt = "expected '='";
 140+ break;
 141+ case close_brace_expected:
 142+ errtxt = "expected '}'";
 143+ break;
 144+ case open_brace_expected:
 145+ errtxt = "expected '{'";
 146+ break;
 147+ }
 148+
 149+ wlog.error(format("%s(%d): %s") % pos.file % pos.line % errtxt);
 150+ wlog.error('\t' + err.where.get_line(pos.line));
 151+ wlog.error(format("\t%s^") % string(pos.col - 1, ' '));
 152+
 153+ while (*scan.first != ';')
 154+ ++scan.first;
 155+
 156+ ++scan.first;
 157+
 158+ return error_status<>(error_status<>::fail);
 159+ }
 160+};
 161+
 162+struct conf_parser_impl : public grammar<conf_parser_impl, file_closure::context_t> {
 163+ struct comment_parser {
 164+ comment_parser() {
 165+ comment = comment_p("/*", "*/") | comment_p("#");
 166+BOOST_SPIRIT_DEBUG_RULE(comment);
 167+ }
 168+
 169+ rule<> const &start(void) const {
 170+ return comment;
 171+ }
 172+
 173+ rule<> comment;
 174+ };
 175+
 176+ symbols<int> times;
 177+ symbols<int> sizes;
 178+
 179+ conf_parser_impl() {
 180+ times.add("second", 1);
 181+ times.add("minute", 60);
 182+ times.add("hour", 60 * 60);
 183+ times.add("day", 60 * 60 * 24);
 184+ times.add("week", 60 * 60 * 24 * 7);
 185+ times.add("fortnight", 60 * 60 * 24 * 7 * 2);
 186+
 187+ sizes.add("byte", 1);
 188+
 189+ sizes.add("kilobyte", 1024);
 190+ sizes.add("megabyte", 1024 * 1024);
 191+ sizes.add("gigabyte", 1024 * 1024 * 1024);
 192+
 193+ sizes.add("kbyte", 1024);
 194+ sizes.add("mbyte", 1024 * 1024);
 195+ sizes.add("gbyte", 1024 * 1024 * 1024);
 196+
 197+ sizes.add("kb", 1024);
 198+ sizes.add("mb", 1024 * 1024);
 199+ sizes.add("gb", 1024 * 1024 * 1024);
 200+ }
 201+
 202+ template<typename scanner>
 203+ struct definition {
 204+ rule<scanner> const &start(void) const {
 205+ return file;
 206+ }
 207+
 208+ assertion<errors> expect_semicolon;
 209+ assertion<errors> expect_close_brace;
 210+ assertion<errors> expect_equals;
 211+ assertion<errors> expect_open_brace;
 212+
 213+ struct push_back_impl {
 214+ template<typename C, typename I>
 215+ struct result {
 216+ typedef void type;
 217+ };
 218+
 219+ template<typename C, typename I>
 220+ void operator() (C &c, I const &i) const {
 221+ c.push_back(i);
 222+ }
 223+ };
 224+
 225+ rule<scanner> tstring, qstring, semicolon, equals,
 226+ close_brace, open_brace;
 227+ rule<scanner> file, require_value;
 228+ rule<scanner, time_closure::context_t> time_m, atime;
 229+ rule<scanner, size_closure::context_t> size_m, asize;
 230+ rule<scanner, timev_closure::context_t> time;
 231+ rule<scanner, sizev_closure::context_t> size;
 232+ rule<scanner, value_closure::context_t> value;
 233+ rule<scanner, block_closure::context_t> block;
 234+ rule<scanner, avalue_closure::context_t> avalue;
 235+
 236+ error_handler err;
 237+ guard<errors> errguard;
 238+ phoenix::function<push_back_impl> const push_back;
 239+
 240+ definition(conf_parser_impl const &self)
 241+ : expect_semicolon(semicolon_expected)
 242+ , expect_close_brace(close_brace_expected)
 243+ , expect_equals(equals_expected)
 244+ , expect_open_brace(open_brace_expected)
 245+ , push_back(push_back_impl())
 246+ {
 247+/*
 248+ * Conf grammar definition.
 249+ */
 250+tstring = lexeme_d[chset<>("a-zA-Z_") >> *chset<>("a-zA-Z0-9_-")];
 251+qstring = lexeme_d[confix_p('"', *c_escape_ch_p, '"')];
 252+semicolon = expect_semicolon(ch_p(';'));
 253+equals = expect_equals(ch_p('='));
 254+open_brace = expect_open_brace(ch_p('{'));
 255+close_brace = expect_close_brace(ch_p('}'));
 256+
 257+file = *block;
 258+require_value = value;
 259+
 260+block = (tstring[block.name = construct_<string>(arg1, arg2)]
 261+ >> !confix_p('"', (*c_escape_ch_p)[block.key = construct_<string>(arg1, arg2)], '"')
 262+ >> errguard(open_brace)[err]
 263+ >> *errguard(require_value)[err]
 264+ >> errguard(close_brace)[err]
 265+ >> errguard(semicolon)[err])[push_back(self.blocks,
 266+ construct_<struct block>(block.name,
 267+ block.key,
 268+ block.values))];
 269+
 270+value = tstring [value.name = construct_<string>(arg1, arg2)]
 271+ >> errguard(equals)[err]
 272+ >> errguard(list_p(avalue, ','))[err]
 273+ >> errguard(semicolon)[err] [push_back(block.values,
 274+ construct_<value_t>(value.name, value.values))];
 275+
 276+avalue = (size [push_back(value.values, avalue.value)])
 277+ | (time [push_back(value.values, avalue.value)])
 278+ | str_p("yes") [push_back(value.values, true)]
 279+ | str_p("no") [push_back(value.values, false)]
 280+ | int_p [push_back(value.values, arg1)]
 281+ | tstring [push_back(value.values, construct_<u_string>(arg1, arg2))]
 282+ | confix_p('"', (*c_escape_ch_p)
 283+ [push_back(value.values, construct_<q_string>(arg1, arg2))], '"')
 284+ ;
 285+
 286+/* size values: "30 megabytes" */
 287+size = size_m[avalue.value = construct_<avalue_t>(size.val)];
 288+
 289+asize = int_p[size_m.value = construct_<size_q>(arg1)] >>
 290+ lexeme_d
 291+ [
 292+ as_lower_d
 293+ [
 294+ self.sizes[size_m.multiplier = construct_<scalar_q>(arg1)]
 295+ ]
 296+ >> !ch_p('s')
 297+ ]
 298+ ;
 299+
 300+size_m = +(asize [size.val += size_m.value * size_m.multiplier]);
 301+
 302+/* time values: "1 hour 30 seconds" */
 303+time = time_m[avalue.value = construct_<avalue_t>(time.val)];
 304+
 305+atime = int_p[time_m.value = construct_<time_q>(arg1)] >>
 306+ lexeme_d
 307+ [
 308+ as_lower_d
 309+ [
 310+ self.times[time_m.multiplier = construct_<scalar_q>(arg1)]
 311+ ]
 312+ >> !ch_p('s')
 313+ ];
 314+
 315+time_m = +(atime [time.val += time_m.value * time_m.multiplier]);
 316+
 317+/**/
 318+
 319+BOOST_SPIRIT_DEBUG_RULE(block);
 320+BOOST_SPIRIT_DEBUG_RULE(string);
 321+BOOST_SPIRIT_DEBUG_RULE(qstring);
 322+BOOST_SPIRIT_DEBUG_RULE(value);
 323+BOOST_SPIRIT_DEBUG_RULE(avalue);
 324+BOOST_SPIRIT_DEBUG_RULE(file);
 325+BOOST_SPIRIT_DEBUG_RULE(size);
 326+BOOST_SPIRIT_DEBUG_RULE(time);
 327+ }
 328+ };
 329+};
 330+
 331+confgrammar::confgrammar(expr::parser const &expr)
 332+ : _expr(expr)
 333+{
 334+}
 335+
 336+vector<block>
 337+confgrammar::parse(string const &file)
 338+{
 339+ typedef char char_t;
 340+ typedef preprocessor::iterator iterator_t;
 341+ typedef scanner<iterator_t> scanner_t;
 342+ conf_parser_impl g;
 343+ conf_parser_impl::comment_parser skip;
 344+
 345+ vector<block> result;
 346+
 347+ error_handler err;
 348+
 349+ try {
 350+ preprocessor p(file, _expr);
 351+
 352+ parse_info<iterator_t> info = spirit::parse(p.begin(), p.end(),
 353+ g[var(result) = arg1],
 354+ comment_p("/*", "*/") | comment_p("#") | chset<>("\n\t "));
 355+ if (!info.full)
 356+ throw parser_error("failed to parse configuration file");
 357+ } catch (exception &e) {
 358+ throw parser_error(e.what());
 359+ }
 360+
 361+ return result;
 362+}
Property changes on: trunk/willow/src/willow/confgrammar.cc
___________________________________________________________________
Added: svn:keywords
1363 + Id Revision
Index: trunk/willow/src/willow/Makefile.in
@@ -21,6 +21,7 @@
2222 cachedir_data_store.cc \
2323 chunking.cc \
2424 config.cc \
 25+ confgrammar.cc \
2526 confparse.cc \
2627 dbwrap.cc \
2728 expr.cc \
@@ -32,36 +33,24 @@
3334 ifname_to_address.cc \
3435 log.cc \
3536 net.cc \
 37+ preprocessor.cc \
3638 radix.cc \
3739 willow.cc \
3840
3941 OBJS= $(BASESRCS:.cc=.o)
4042
4143 SRCS=$(BASESRCS)
42 -OBJADD = @LIBOBJS@ y.tab.o lex.yy.o
 44+OBJADD = @LIBOBJS@
4345
4446 EXTRA_DIST= strlcat.c strlcpy.c \
45 - lexer.l parser.y \
4647 daemon.c \
4748 Makefile.in
4849
4950 LDFLAGS = $(LIBOBJS) -L../libwillow -lwillow
5051 EXTRADEPS=../libwillow/libwillow.a
5152
52 -.SUFFIXES: .y .l .c .cc .o
 53+.SUFFIXES: .c .cc .o
5354
5455 default: all
5556
56 -y.tab.o: y.tab.cc ../include/confparse.h ../include/willow.h
57 -lex.yy.o: lex.yy.cc y.tab.h ../include/confparse.h ../include/willow.h
58 -
59 -y.tab.cc y.tab.h: parser.y
60 - @echo " $(_YACC) -d parser.y"
61 - @$(_YACC) -d parser.y
62 - @mv y.tab.c y.tab.cc
63 -lex.yy.cc: lexer.l y.tab.h
64 - @echo " $(_LEX) lexer.l"
65 - @$(_LEX) lexer.l || rm -f lex.yy.c
66 - @test -f lex.yy.c && mv lex.yy.c lex.yy.cc
67 -
6857 @include@ @q@@top_srcdir@/mk/prog.mk@q@
Index: trunk/willow/src/willow/config.cc
@@ -574,7 +574,7 @@
575575
576576 if (!read_config(file)) {
577577 wlog.error("cannot load configuration");
578 - nerrors++;
 578+ exit(8);
579579 }
580580
581581 if (!listeners.size()) {
Index: trunk/willow/src/willow/expr.cc
@@ -12,7 +12,7 @@
1313
1414 #include "autoconf.h"
1515
16 -#ifdef WILLOW_DEBUG
 16+#if defined(WILLOW_DEBUG) && 0
1717 # define BOOST_SPIRIT_DEBUG
1818 #endif
1919
@@ -175,33 +175,30 @@
176176
177177 namespace expr {
178178
179 -using expr::expression_parser;
180 -using expr::expression_error;
181 -
182 -expression_parser::expression_parser()
 179+expr::parser::parser()
183180 {
184181 impl = new expression_parser_impl;
185182 }
186183
187 -expression_parser::~expression_parser()
 184+expr::parser::~parser()
188185 {
189186 delete impl;
190187 }
191188
192189 void
193 -expression_parser::add_variable(string const &name, int64_t val)
 190+expr::parser::add_variable(string const &name, int64_t val)
194191 {
195192 impl->add_variable(name, val);
196193 }
197194
198195 bool
199 -expression_parser::variable_defined(string const &name) const
 196+expr::parser::variable_defined(string const &name) const
200197 {
201198 return impl->variable_defined(name);
202199 }
203200
204201 int64_t
205 -expression_parser::run(string const &str) const
 202+expr::parser::run(string const &str) const
206203 {
207204 return impl->run(str);
208205 }
Index: trunk/willow/src/willow/confparse.cc
@@ -29,6 +29,7 @@
3030 #define NEED_PARSING_TREE
3131
3232 #include "confparse.h"
 33+#include "confgrammar.h"
3334 #include "willow.h"
3435 #include "log.h"
3536 #include "backend.h"
@@ -38,7 +39,7 @@
3940
4041 namespace conf {
4142
42 -expr::expression_parser if_parser;
 43+expr::parser if_parser;
4344
4445 tree global_conf_tree;
4546 map<string, value> variable_list;
@@ -78,6 +79,7 @@
7980 add_if_entry(unm.sysname, 1);
8081 }
8182
 83+#if 0
8284 if ((yyin = fopen(file.c_str(), "r")) == NULL) {
8385 wlog.error(format("could not open configuration file %s: %s")
8486 % file % strerror(errno));
@@ -85,6 +87,77 @@
8688 }
8789 if (yyparse() || parse_error)
8890 return NULL;
 91+#endif
 92+
 93+ confgrammar g(if_parser);
 94+ vector<block> result;
 95+
 96+ try {
 97+ result = g.parse(file);
 98+ } catch (parser_error &e) {
 99+ wlog.error(format("cannot parse configuration file: %s")
 100+ % e.what());
 101+ return NULL;
 102+ }
 103+
 104+ vector<block>::iterator blockit, blockend;
 105+ vector<value_t>::iterator valueit, valueend;
 106+ vector<avalue_t>::iterator avalueit, avalueend;
 107+
 108+ for (blockit = result.begin(), blockend = result.end();
 109+ blockit != blockend; ++blockit) {
 110+ tree_entry e = declpos();
 111+ e.item_name = blockit->name;
 112+ e.item_key = blockit->key;
 113+
 114+ for (valueit = blockit->values.begin(),
 115+ valueend = blockit->values.end();
 116+ valueit != valueend; ++valueit) {
 117+ value v = declpos();
 118+ v.cv_name = valueit->name;
 119+
 120+ for (avalueit = valueit->values.begin(),
 121+ avalueend = valueit->values.end();
 122+ avalueit != avalueend; ++avalueit) {
 123+ avalue av;
 124+ switch (avalueit->which()) {
 125+ case 0: // bare string
 126+ av.av_type = cv_string;
 127+ av.av_strval = boost::get<u_string>(*avalueit).value();
 128+ break;
 129+
 130+ case 1: // quoted string
 131+ av.av_type = cv_qstring;
 132+ av.av_strval = boost::get<q_string>(*avalueit).value();
 133+ break;
 134+
 135+ case 2: // yesno
 136+ av.av_type = cv_yesno;
 137+ av.av_intval = boost::get<bool>(*avalueit);
 138+ break;
 139+
 140+ case 3: // int
 141+ av.av_type = cv_int;
 142+ av.av_intval = boost::get<int>(*avalueit);
 143+ break;
 144+
 145+ case 4: // time
 146+ av.av_type = cv_time;
 147+ av.av_intval = boost::get<time_q>(*avalueit).value();
 148+ break;
 149+
 150+ case 5: // size
 151+ av.av_type = cv_time;
 152+ av.av_intval = boost::get<size_q>(*avalueit).value();
 153+ break;
 154+ }
 155+ v.cv_values.push_back(av);
 156+ }
 157+ e.add(v);
 158+ }
 159+ parsing_tree.add(e);
 160+ }
 161+
89162 return &parsing_tree;
90163 }
91164
@@ -146,7 +219,7 @@
147220
148221 try {
149222 return if_parser.run(dir);
150 - } catch (expr::expression_error &e) {
 223+ } catch (expr::error &e) {
151224 report_parse_error("error in %%if expression: %s", e.what());
152225 return false;
153226 }
Index: trunk/willow/src/willow/log.cc
@@ -92,7 +92,7 @@
9393 }
9494
9595 if (config.foreground)
96 - cout << r << '\n';
 96+ cout << sev_names[sev] << ": " << e << '\n';
9797 }
9898
9999 void
Index: trunk/willow/src/willow/preprocessor.cc
@@ -0,0 +1,312 @@
 2+/* Willow: Lightweight HTTP reverse-proxy. */
 3+/* preprocessor: cpp-style preprocessor for config parsing */
 4+/* Copyright (c) 2005, 2006 River Tarnell <river@attenuate.org>. */
 5+/*
 6+ * Permission is granted to anyone to use this software for any purpose,
 7+ * including commercial applications, and to alter it and redistribute it
 8+ * freely. This software is provided 'as-is', without any express or implied
 9+ * warranty.
 10+ */
 11+
 12+/* $Id$ */
 13+
 14+#include <utility>
 15+#include <boost/spirit.hpp>
 16+using std::pair;
 17+using std::make_pair;
 18+using boost::spirit::file_iterator;
 19+
 20+#include "preprocessor.h"
 21+
 22+file_position::file_position(string const &f, int l, int p)
 23+ : file(f), line(l), col(p) {}
 24+
 25+file_position::file_position(file_position const &o)
 26+ : file(o.file), line(o.line), col(o.col) {}
 27+
 28+file_position &
 29+file_position::operator= (file_position const &o) {
 30+ file = o.file;
 31+ line = o.line;
 32+ col = o.col;
 33+ return *this;
 34+}
 35+
 36+ostream&
 37+operator<< (ostream &o, file_position const &p) {
 38+ o << p.file << '(' << p.line << ')';
 39+ return o;
 40+}
 41+
 42+preprocessor::iterator::iterator()
 43+ : _cpp(NULL) {}
 44+
 45+preprocessor::iterator::iterator(preprocessor const &cpp,
 46+ vector<char>::const_iterator it)
 47+ : _cpp(&cpp)
 48+ , _it(it) {
 49+}
 50+
 51+preprocessor::iterator::iterator(preprocessor::iterator::iterator const &other)
 52+ : _cpp(other._cpp)
 53+ , _it(other._it) {
 54+}
 55+
 56+preprocessor::iterator &
 57+preprocessor::iterator::operator= (preprocessor::iterator const &other) {
 58+ _cpp = other._cpp;
 59+ _it = other._it;
 60+ return *this;
 61+}
 62+
 63+preprocessor::iterator &
 64+preprocessor::iterator::operator++(void) {
 65+ ++_it;
 66+ return *this;
 67+}
 68+
 69+preprocessor::iterator
 70+preprocessor::iterator::operator++(int) {
 71+iterator ret (*this);
 72+ ++_it;
 73+ return ret;
 74+}
 75+
 76+preprocessor::iterator &
 77+preprocessor::iterator::operator--(void) {
 78+ --_it;
 79+ return *this;
 80+}
 81+
 82+preprocessor::iterator
 83+preprocessor::iterator::operator--(int) {
 84+iterator ret (*this);
 85+ --_it;
 86+ return ret;
 87+}
 88+
 89+preprocessor::iterator::reference
 90+preprocessor::iterator::operator*(void) const {
 91+ return *_it;
 92+}
 93+
 94+bool
 95+preprocessor::iterator::operator< (preprocessor::iterator const &other) const {
 96+ return _it < other._it;
 97+}
 98+
 99+bool
 100+preprocessor::iterator::operator== (preprocessor::iterator const &other) const {
 101+ return !(*this < other) && !(other < *this);
 102+}
 103+
 104+bool
 105+preprocessor::iterator::operator!= (preprocessor::iterator const &other) const {
 106+ return !(*this == other);
 107+}
 108+
 109+bool
 110+preprocessor::iterator::operator> (preprocessor::iterator const &other) const {
 111+ return !(*this < other) && !(*this == other);
 112+}
 113+
 114+bool
 115+preprocessor::iterator::operator<= (preprocessor::iterator const &other) const {
 116+ return (*this < other) || (*this == other);
 117+}
 118+
 119+bool
 120+preprocessor::iterator::operator>= (preprocessor::iterator const &other) const {
 121+ return (*this > other) || (*this == other);
 122+}
 123+
 124+preprocessor::iterator::difference_type
 125+preprocessor::iterator::operator- (preprocessor::iterator const &other) const {
 126+ return _it - other._it;
 127+}
 128+
 129+preprocessor::iterator
 130+preprocessor::iterator::operator- (size_type s) const {
 131+ return iterator(*_cpp, _it - s);
 132+}
 133+
 134+preprocessor::iterator
 135+preprocessor::iterator::operator+ (size_type s) const {
 136+ return iterator(*_cpp, _it + s);
 137+}
 138+
 139+file_position
 140+preprocessor::iterator::get_position(void) const {
 141+ return _cpp->get_position(_it);
 142+}
 143+
 144+string const &
 145+preprocessor::iterator::get_line(int i) const {
 146+ return _cpp->get_line(i);
 147+}
 148+
 149+void
 150+preprocessor::set_position(vector<char>::const_iterator n)
 151+{
 152+ positions.insert(make_pair(distance(
 153+ static_cast<vector<char> const &>(_data).begin(), n),
 154+ file_position(_curfile, _curline, _curpos)));
 155+}
 156+
 157+string const &
 158+preprocessor::get_line(int line) const
 159+{
 160+ return lines[line - 1];
 161+}
 162+
 163+file_position
 164+preprocessor::get_position(vector<char>::const_iterator it) const
 165+{
 166+map<vector<char>::difference_type, file_position>::const_iterator posit;
 167+
 168+ posit = positions.upper_bound(distance(_data.begin(), it));
 169+ assert(posit != positions.end());
 170+
 171+file_position p(posit->second);
 172+vector<char>::const_iterator lit = it;
 173+
 174+ p.col = 0;
 175+ for (; lit > _data.begin() && *lit != '\n'; --lit)
 176+ if (*lit == '\t')
 177+ p.col += 8;
 178+ else
 179+ ++p.col;
 180+ return p;
 181+}
 182+
 183+preprocessor::preprocessor(string const &file, expr::parser const &expr)
 184+ : _curfile(file)
 185+ , _curline(1)
 186+ , _curpos(0)
 187+ , _expr(expr)
 188+{
 189+ file_iterator<> first(file.c_str());
 190+ file_iterator<> last = first.make_end();
 191+ process(first, last);
 192+ _end = iterator(*this, _data.end());
 193+}
 194+
 195+preprocessor::iterator
 196+preprocessor::begin(void)
 197+{
 198+ return iterator(*this, _data.begin());
 199+}
 200+
 201+preprocessor::const_iterator
 202+preprocessor::begin(void) const
 203+{
 204+ return iterator(*this, _data.begin());
 205+}
 206+
 207+preprocessor::iterator
 208+preprocessor::end(void)
 209+{
 210+ return iterator(*this, _data.end());
 211+}
 212+
 213+preprocessor::const_iterator
 214+preprocessor::end(void) const
 215+{
 216+ return iterator(*this, _data.end());
 217+}
 218+
 219+template<typename iterT>
 220+void
 221+preprocessor::process(iterT begin, iterT end)
 222+{
 223+bool ateol = false;
 224+bool ignoring = false;
 225+iterT lbegin = begin;
 226+ for (; begin != end; ++begin) {
 227+ switch (*begin) {
 228+ case '%': {
 229+ /* everything until the end of the line is a directive */
 230+ if (ateol) {
 231+ iterT eol = find(begin, end, '\n');
 232+ string expr(begin, eol);
 233+ ignoring = evaluate_expr(begin, eol, ignoring);
 234+ lines.push_back(string(lbegin, begin));
 235+ set_position(_data.end());
 236+ begin = eol;
 237+ lbegin = begin + 1;
 238+ _curline++;
 239+ _curpos = 0;
 240+ break;
 241+ }
 242+ goto normal;
 243+ }
 244+
 245+ case '\n':
 246+ ateol = true;
 247+ lines.push_back(string(lbegin, begin));
 248+ set_position(_data.end());
 249+ lbegin = begin + 1;
 250+ _curline++;
 251+ _curpos = 0;
 252+ _data.push_back(*begin);
 253+ break;
 254+
 255+ normal:
 256+ default:
 257+ if (!ignoring) {
 258+ _data.push_back(*begin);
 259+ }
 260+ if (*begin == '\t')
 261+ _curpos += 8;
 262+ else
 263+ ++_curpos;
 264+ ateol = false;
 265+ break;
 266+ }
 267+ }
 268+}
 269+
 270+template<typename iterT>
 271+bool
 272+preprocessor::evaluate_expr(iterT begin, iterT end, bool ignoring)
 273+{
 274+iterT space = find(begin, end, ' ');
 275+ if (itercmp(begin, space, "%if")) {
 276+ if (space == end)
 277+ return false;
 278+
 279+ string expr(space + 1, end);
 280+ try {
 281+ if (_expr.run(expr))
 282+ return false;
 283+ } catch (expr::error &e) {
 284+ wlog.error(format("%s(%d): error parsing expression: %s")
 285+ % _curfile % _curline % e.what());
 286+ wlog.error('\t' + string(begin, end) + '\n');
 287+ throw preprocessor_exception(e.what());
 288+ }
 289+
 290+ return true;
 291+ } else if (itercmp(begin, space, "%else")) {
 292+ return !ignoring;
 293+ } else if (itercmp(begin, space, "%endif")) {
 294+ return false;
 295+ } else {
 296+ wlog.error(format("%s(%d): unrecognised directive")
 297+ % _curfile % _curline);
 298+ wlog.error('\t' + string(begin, end) + '\n');
 299+ throw preprocessor_exception(
 300+ "syntax error in preprocessor directive");
 301+ }
 302+ return false;
 303+}
 304+
 305+template<typename iterT>
 306+bool
 307+preprocessor::itercmp(iterT begin, iterT end, char const *s)
 308+{
 309+int len = strlen(s);
 310+ if (distance(begin, end) != len)
 311+ return false;
 312+ return equal(begin, end, s);
 313+}
Property changes on: trunk/willow/src/willow/preprocessor.cc
___________________________________________________________________
Added: svn:keywords
1314 + Id Revision