aboutsummaryrefslogtreecommitdiffstats
path: root/edify
diff options
context:
space:
mode:
authorDoug Zongker2009-06-12 14:24:39 -0500
committerDoug Zongker2009-06-12 16:05:03 -0500
commitd9c9d10d9da76f067d3955bea71f7bb39e859fa5 (patch)
tree1e49a3a616c3147f871e79b1b15e2b2a63379cc1 /edify
parent8edb00c990e563e6f91b278a212f2edf877cf763 (diff)
downloadplatform-bootable-recovery-d9c9d10d9da76f067d3955bea71f7bb39e859fa5.tar.gz
platform-bootable-recovery-d9c9d10d9da76f067d3955bea71f7bb39e859fa5.tar.xz
platform-bootable-recovery-d9c9d10d9da76f067d3955bea71f7bb39e859fa5.zip
fixes to edify and updater script
A few more changes to edify: - fix write_raw_image(); my last change neglected to close the write context, so the written image was corrupt. - each expression tracks the span of the source code from which it was compiled, so that assert()'s error message can include the source of the expression that failed. - the 'cookie' argument to each Function is replaced with a State object, which contains the cookie, the source script (for use with the above spans), and the current error message (replacing the global variables that were used for this purpose). - in the recovery image, a new command "ui_print" can be sent back through the command pipe to cause text to appear on the screen. Add a new ui_print() function to print things from scripts. Rename existing "print" function to "stdout".
Diffstat (limited to 'edify')
-rw-r--r--edify/expr.c506
-rw-r--r--edify/expr.h61
-rw-r--r--edify/lexer.l47
-rw-r--r--edify/main.c301
-rw-r--r--edify/parser.y31
-rw-r--r--edify/yydefs.h36
6 files changed, 540 insertions, 442 deletions
diff --git a/edify/expr.c b/edify/expr.c
index 5470a2ba..406c67ea 100644
--- a/edify/expr.c
+++ b/edify/expr.c
@@ -29,249 +29,241 @@
29// - if Evaluate() on any argument returns NULL, return NULL. 29// - if Evaluate() on any argument returns NULL, return NULL.
30 30
31int BooleanString(const char* s) { 31int BooleanString(const char* s) {
32 return s[0] != '\0'; 32 return s[0] != '\0';
33} 33}
34 34
35char* Evaluate(void* cookie, Expr* expr) { 35char* Evaluate(State* state, Expr* expr) {
36 return expr->fn(expr->name, cookie, expr->argc, expr->argv); 36 return expr->fn(expr->name, state, expr->argc, expr->argv);
37} 37}
38 38
39char* ConcatFn(const char* name, void* cookie, int argc, Expr* argv[]) { 39char* ConcatFn(const char* name, State* state, int argc, Expr* argv[]) {
40 if (argc == 0) { 40 if (argc == 0) {
41 return strdup(""); 41 return strdup("");
42 } 42 }
43 char** strings = malloc(argc * sizeof(char*)); 43 char** strings = malloc(argc * sizeof(char*));
44 int i; 44 int i;
45 for (i = 0; i < argc; ++i) { 45 for (i = 0; i < argc; ++i) {
46 strings[i] = NULL; 46 strings[i] = NULL;
47 } 47 }
48 char* result = NULL; 48 char* result = NULL;
49 int length = 0; 49 int length = 0;
50 for (i = 0; i < argc; ++i) { 50 for (i = 0; i < argc; ++i) {
51 strings[i] = Evaluate(cookie, argv[i]); 51 strings[i] = Evaluate(state, argv[i]);
52 if (strings[i] == NULL) { 52 if (strings[i] == NULL) {
53 goto done; 53 goto done;
54 }
55 length += strlen(strings[i]);
56 }
57
58 result = malloc(length+1);
59 int p = 0;
60 for (i = 0; i < argc; ++i) {
61 strcpy(result+p, strings[i]);
62 p += strlen(strings[i]);
63 }
64 result[p] = '\0';
65
66 done:
67 for (i = 0; i < argc; ++i) {
68 free(strings[i]);
54 } 69 }
55 length += strlen(strings[i]); 70 return result;
56 }
57
58 result = malloc(length+1);
59 int p = 0;
60 for (i = 0; i < argc; ++i) {
61 strcpy(result+p, strings[i]);
62 p += strlen(strings[i]);
63 }
64 result[p] = '\0';
65
66done:
67 for (i = 0; i < argc; ++i) {
68 free(strings[i]);
69 }
70 return result;
71} 71}
72 72
73char* IfElseFn(const char* name, void* cookie, int argc, Expr* argv[]) { 73char* IfElseFn(const char* name, State* state, int argc, Expr* argv[]) {
74 if (argc != 2 && argc != 3) { 74 if (argc != 2 && argc != 3) {
75 return NULL; 75 return NULL;
76 } 76 }
77 char* cond = Evaluate(cookie, argv[0]); 77 char* cond = Evaluate(state, argv[0]);
78 if (cond == NULL) { 78 if (cond == NULL) {
79 return NULL; 79 return NULL;
80 } 80 }
81 81
82 if (BooleanString(cond) == true) { 82 if (BooleanString(cond) == true) {
83 free(cond); 83 free(cond);
84 return Evaluate(cookie, argv[1]); 84 return Evaluate(state, argv[1]);
85 } else {
86 if (argc == 3) {
87 free(cond);
88 return Evaluate(cookie, argv[2]);
89 } else { 85 } else {
90 return cond; 86 if (argc == 3) {
87 free(cond);
88 return Evaluate(state, argv[2]);
89 } else {
90 return cond;
91 }
91 } 92 }
92 }
93} 93}
94 94
95char* AbortFn(const char* name, void* cookie, int argc, Expr* argv[]) { 95char* AbortFn(const char* name, State* state, int argc, Expr* argv[]) {
96 char* msg = NULL; 96 char* msg = NULL;
97 if (argc > 0) { 97 if (argc > 0) {
98 msg = Evaluate(cookie, argv[0]); 98 msg = Evaluate(state, argv[0]);
99 } 99 }
100 SetError(msg == NULL ? "called abort()" : msg); 100 free(state->errmsg);
101 free(msg); 101 if (msg) {
102 return NULL; 102 state->errmsg = msg;
103 } else {
104 state->errmsg = strdup("called abort()");
105 }
106 return NULL;
103} 107}
104 108
105char* AssertFn(const char* name, void* cookie, int argc, Expr* argv[]) { 109char* AssertFn(const char* name, State* state, int argc, Expr* argv[]) {
106 int i; 110 int i;
107 for (i = 0; i < argc; ++i) { 111 for (i = 0; i < argc; ++i) {
108 char* v = Evaluate(cookie, argv[i]); 112 char* v = Evaluate(state, argv[i]);
109 if (v == NULL) { 113 if (v == NULL) {
110 return NULL; 114 return NULL;
111 } 115 }
112 int b = BooleanString(v); 116 int b = BooleanString(v);
113 free(v); 117 free(v);
114 if (!b) { 118 if (!b) {
115 SetError("assert() failed"); 119 int prefix_len;
116 return NULL; 120 int len = argv[i]->end - argv[i]->start;
121 char* err_src = malloc(len + 20);
122 strcpy(err_src, "assert failed: ");
123 prefix_len = strlen(err_src);
124 memcpy(err_src + prefix_len, state->script + argv[i]->start, len);
125 err_src[prefix_len + len] = '\0';
126 free(state->errmsg);
127 state->errmsg = err_src;
128 return NULL;
129 }
117 } 130 }
118 } 131 return strdup("");
119 return strdup("");
120} 132}
121 133
122char* SleepFn(const char* name, void* cookie, int argc, Expr* argv[]) { 134char* SleepFn(const char* name, State* state, int argc, Expr* argv[]) {
123 char* val = Evaluate(cookie, argv[0]); 135 char* val = Evaluate(state, argv[0]);
124 if (val == NULL) { 136 if (val == NULL) {
125 return NULL; 137 return NULL;
126 } 138 }
127 int v = strtol(val, NULL, 10); 139 int v = strtol(val, NULL, 10);
128 sleep(v); 140 sleep(v);
129 return val; 141 return val;
130} 142}
131 143
132char* PrintFn(const char* name, void* cookie, int argc, Expr* argv[]) { 144char* StdoutFn(const char* name, State* state, int argc, Expr* argv[]) {
133 int i; 145 int i;
134 for (i = 0; i < argc; ++i) { 146 for (i = 0; i < argc; ++i) {
135 char* v = Evaluate(cookie, argv[i]); 147 char* v = Evaluate(state, argv[i]);
136 if (v == NULL) { 148 if (v == NULL) {
137 return NULL; 149 return NULL;
150 }
151 fputs(v, stdout);
152 free(v);
138 } 153 }
139 fputs(v, stdout); 154 return strdup("");
140 free(v);
141 }
142 return strdup("");
143} 155}
144 156
145char* LogicalAndFn(const char* name, void* cookie, 157char* LogicalAndFn(const char* name, State* state,
146 int argc, Expr* argv[]) { 158 int argc, Expr* argv[]) {
147 char* left = Evaluate(cookie, argv[0]); 159 char* left = Evaluate(state, argv[0]);
148 if (left == NULL) return NULL; 160 if (left == NULL) return NULL;
149 if (BooleanString(left) == true) { 161 if (BooleanString(left) == true) {
150 free(left); 162 free(left);
151 return Evaluate(cookie, argv[1]); 163 return Evaluate(state, argv[1]);
152 } else { 164 } else {
153 return left; 165 return left;
154 } 166 }
155} 167}
156 168
157char* LogicalOrFn(const char* name, void* cookie, 169char* LogicalOrFn(const char* name, State* state,
158 int argc, Expr* argv[]) { 170 int argc, Expr* argv[]) {
159 char* left = Evaluate(cookie, argv[0]); 171 char* left = Evaluate(state, argv[0]);
160 if (left == NULL) return NULL; 172 if (left == NULL) return NULL;
161 if (BooleanString(left) == false) { 173 if (BooleanString(left) == false) {
162 free(left); 174 free(left);
163 return Evaluate(cookie, argv[1]); 175 return Evaluate(state, argv[1]);
164 } else { 176 } else {
165 return left; 177 return left;
166 } 178 }
167} 179}
168 180
169char* LogicalNotFn(const char* name, void* cookie, 181char* LogicalNotFn(const char* name, State* state,
170 int argc, Expr* argv[]) { 182 int argc, Expr* argv[]) {
171 char* val = Evaluate(cookie, argv[0]); 183 char* val = Evaluate(state, argv[0]);
172 if (val == NULL) return NULL; 184 if (val == NULL) return NULL;
173 bool bv = BooleanString(val); 185 bool bv = BooleanString(val);
174 free(val); 186 free(val);
175 if (bv) { 187 if (bv) {
176 return strdup(""); 188 return strdup("");
177 } else { 189 } else {
178 return strdup("t"); 190 return strdup("t");
179 } 191 }
180} 192}
181 193
182char* SubstringFn(const char* name, void* cookie, 194char* SubstringFn(const char* name, State* state,
183 int argc, Expr* argv[]) { 195 int argc, Expr* argv[]) {
184 char* needle = Evaluate(cookie, argv[0]); 196 char* needle = Evaluate(state, argv[0]);
185 if (needle == NULL) return NULL; 197 if (needle == NULL) return NULL;
186 char* haystack = Evaluate(cookie, argv[1]); 198 char* haystack = Evaluate(state, argv[1]);
187 if (haystack == NULL) { 199 if (haystack == NULL) {
188 free(needle); 200 free(needle);
189 return NULL; 201 return NULL;
190 } 202 }
191 203
192 char* result = strdup(strstr(haystack, needle) ? "t" : ""); 204 char* result = strdup(strstr(haystack, needle) ? "t" : "");
193 free(needle); 205 free(needle);
194 free(haystack); 206 free(haystack);
195 return result; 207 return result;
196} 208}
197 209
198char* EqualityFn(const char* name, void* cookie, int argc, Expr* argv[]) { 210char* EqualityFn(const char* name, State* state, int argc, Expr* argv[]) {
199 char* left = Evaluate(cookie, argv[0]); 211 char* left = Evaluate(state, argv[0]);
200 if (left == NULL) return NULL; 212 if (left == NULL) return NULL;
201 char* right = Evaluate(cookie, argv[1]); 213 char* right = Evaluate(state, argv[1]);
202 if (right == NULL) { 214 if (right == NULL) {
203 free(left); 215 free(left);
204 return NULL; 216 return NULL;
205 } 217 }
206
207 char* result = strdup(strcmp(left, right) == 0 ? "t" : "");
208 free(left);
209 free(right);
210 return result;
211}
212 218
213char* InequalityFn(const char* name, void* cookie, int argc, Expr* argv[]) { 219 char* result = strdup(strcmp(left, right) == 0 ? "t" : "");
214 char* left = Evaluate(cookie, argv[0]);
215 if (left == NULL) return NULL;
216 char* right = Evaluate(cookie, argv[1]);
217 if (right == NULL) {
218 free(left); 220 free(left);
219 return NULL; 221 free(right);
220 } 222 return result;
221
222 char* result = strdup(strcmp(left, right) != 0 ? "t" : "");
223 free(left);
224 free(right);
225 return result;
226} 223}
227 224
228char* SequenceFn(const char* name, void* cookie, int argc, Expr* argv[]) { 225char* InequalityFn(const char* name, State* state, int argc, Expr* argv[]) {
229 char* left = Evaluate(cookie, argv[0]); 226 char* left = Evaluate(state, argv[0]);
230 if (left == NULL) return NULL; 227 if (left == NULL) return NULL;
231 free(left); 228 char* right = Evaluate(state, argv[1]);
232 return Evaluate(cookie, argv[1]); 229 if (right == NULL) {
233} 230 free(left);
234 231 return NULL;
235char* Literal(const char* name, void* cookie, int argc, Expr* argv[]) { 232 }
236 return strdup(name);
237}
238 233
239Expr* Build(Function fn, int count, ...) { 234 char* result = strdup(strcmp(left, right) != 0 ? "t" : "");
240 va_list v; 235 free(left);
241 va_start(v, count); 236 free(right);
242 Expr* e = malloc(sizeof(Expr)); 237 return result;
243 e->fn = fn;
244 e->name = "(operator)";
245 e->argc = count;
246 e->argv = malloc(count * sizeof(Expr*));
247 int i;
248 for (i = 0; i < count; ++i) {
249 e->argv[i] = va_arg(v, Expr*);
250 }
251 va_end(v);
252 return e;
253} 238}
254 239
255// ----------------------------------------------------------------- 240char* SequenceFn(const char* name, State* state, int argc, Expr* argv[]) {
256// error reporting 241 char* left = Evaluate(state, argv[0]);
257// ----------------------------------------------------------------- 242 if (left == NULL) return NULL;
258 243 free(left);
259static char* error_message = NULL; 244 return Evaluate(state, argv[1]);
260
261void SetError(const char* message) {
262 if (error_message) {
263 free(error_message);
264 }
265 error_message = strdup(message);
266} 245}
267 246
268const char* GetError() { 247char* Literal(const char* name, State* state, int argc, Expr* argv[]) {
269 return error_message; 248 return strdup(name);
270} 249}
271 250
272void ClearError() { 251Expr* Build(Function fn, YYLTYPE loc, int count, ...) {
273 free(error_message); 252 va_list v;
274 error_message = NULL; 253 va_start(v, count);
254 Expr* e = malloc(sizeof(Expr));
255 e->fn = fn;
256 e->name = "(operator)";
257 e->argc = count;
258 e->argv = malloc(count * sizeof(Expr*));
259 int i;
260 for (i = 0; i < count; ++i) {
261 e->argv[i] = va_arg(v, Expr*);
262 }
263 va_end(v);
264 e->start = loc.start;
265 e->end = loc.end;
266 return e;
275} 267}
276 268
277// ----------------------------------------------------------------- 269// -----------------------------------------------------------------
@@ -283,44 +275,44 @@ static int fn_size = 0;
283NamedFunction* fn_table = NULL; 275NamedFunction* fn_table = NULL;
284 276
285void RegisterFunction(const char* name, Function fn) { 277void RegisterFunction(const char* name, Function fn) {
286 if (fn_entries >= fn_size) { 278 if (fn_entries >= fn_size) {
287 fn_size = fn_size*2 + 1; 279 fn_size = fn_size*2 + 1;
288 fn_table = realloc(fn_table, fn_size * sizeof(NamedFunction)); 280 fn_table = realloc(fn_table, fn_size * sizeof(NamedFunction));
289 } 281 }
290 fn_table[fn_entries].name = name; 282 fn_table[fn_entries].name = name;
291 fn_table[fn_entries].fn = fn; 283 fn_table[fn_entries].fn = fn;
292 ++fn_entries; 284 ++fn_entries;
293} 285}
294 286
295static int fn_entry_compare(const void* a, const void* b) { 287static int fn_entry_compare(const void* a, const void* b) {
296 const char* na = ((const NamedFunction*)a)->name; 288 const char* na = ((const NamedFunction*)a)->name;
297 const char* nb = ((const NamedFunction*)b)->name; 289 const char* nb = ((const NamedFunction*)b)->name;
298 return strcmp(na, nb); 290 return strcmp(na, nb);
299} 291}
300 292
301void FinishRegistration() { 293void FinishRegistration() {
302 qsort(fn_table, fn_entries, sizeof(NamedFunction), fn_entry_compare); 294 qsort(fn_table, fn_entries, sizeof(NamedFunction), fn_entry_compare);
303} 295}
304 296
305Function FindFunction(const char* name) { 297Function FindFunction(const char* name) {
306 NamedFunction key; 298 NamedFunction key;
307 key.name = name; 299 key.name = name;
308 NamedFunction* nf = bsearch(&key, fn_table, fn_entries, sizeof(NamedFunction), 300 NamedFunction* nf = bsearch(&key, fn_table, fn_entries,
309 fn_entry_compare); 301 sizeof(NamedFunction), fn_entry_compare);
310 if (nf == NULL) { 302 if (nf == NULL) {
311 return NULL; 303 return NULL;
312 } 304 }
313 return nf->fn; 305 return nf->fn;
314} 306}
315 307
316void RegisterBuiltins() { 308void RegisterBuiltins() {
317 RegisterFunction("ifelse", IfElseFn); 309 RegisterFunction("ifelse", IfElseFn);
318 RegisterFunction("abort", AbortFn); 310 RegisterFunction("abort", AbortFn);
319 RegisterFunction("assert", AssertFn); 311 RegisterFunction("assert", AssertFn);
320 RegisterFunction("concat", ConcatFn); 312 RegisterFunction("concat", ConcatFn);
321 RegisterFunction("is_substring", SubstringFn); 313 RegisterFunction("is_substring", SubstringFn);
322 RegisterFunction("print", PrintFn); 314 RegisterFunction("stdout", StdoutFn);
323 RegisterFunction("sleep", SleepFn); 315 RegisterFunction("sleep", SleepFn);
324} 316}
325 317
326 318
@@ -331,44 +323,44 @@ void RegisterBuiltins() {
331// Evaluate the expressions in argv, giving 'count' char* (the ... is 323// Evaluate the expressions in argv, giving 'count' char* (the ... is
332// zero or more char** to put them in). If any expression evaluates 324// zero or more char** to put them in). If any expression evaluates
333// to NULL, free the rest and return -1. Return 0 on success. 325// to NULL, free the rest and return -1. Return 0 on success.
334int ReadArgs(void* cookie, Expr* argv[], int count, ...) { 326int ReadArgs(State* state, Expr* argv[], int count, ...) {
335 char** args = malloc(count * sizeof(char*)); 327 char** args = malloc(count * sizeof(char*));
336 va_list v; 328 va_list v;
337 va_start(v, count); 329 va_start(v, count);
338 int i; 330 int i;
339 for (i = 0; i < count; ++i) { 331 for (i = 0; i < count; ++i) {
340 args[i] = Evaluate(cookie, argv[i]); 332 args[i] = Evaluate(state, argv[i]);
341 if (args[i] == NULL) { 333 if (args[i] == NULL) {
342 va_end(v); 334 va_end(v);
343 int j; 335 int j;
344 for (j = 0; j < i; ++j) { 336 for (j = 0; j < i; ++j) {
345 free(args[j]); 337 free(args[j]);
346 } 338 }
347 return -1; 339 return -1;
340 }
341 *(va_arg(v, char**)) = args[i];
348 } 342 }
349 *(va_arg(v, char**)) = args[i]; 343 va_end(v);
350 } 344 return 0;
351 va_end(v);
352 return 0;
353} 345}
354 346
355// Evaluate the expressions in argv, returning an array of char* 347// Evaluate the expressions in argv, returning an array of char*
356// results. If any evaluate to NULL, free the rest and return NULL. 348// results. If any evaluate to NULL, free the rest and return NULL.
357// The caller is responsible for freeing the returned array and the 349// The caller is responsible for freeing the returned array and the
358// strings it contains. 350// strings it contains.
359char** ReadVarArgs(void* cookie, int argc, Expr* argv[]) { 351char** ReadVarArgs(State* state, int argc, Expr* argv[]) {
360 char** args = (char**)malloc(argc * sizeof(char*)); 352 char** args = (char**)malloc(argc * sizeof(char*));
361 int i = 0; 353 int i = 0;
362 for (i = 0; i < argc; ++i) { 354 for (i = 0; i < argc; ++i) {
363 args[i] = Evaluate(cookie, argv[i]); 355 args[i] = Evaluate(state, argv[i]);
364 if (args[i] == NULL) { 356 if (args[i] == NULL) {
365 int j; 357 int j;
366 for (j = 0; j < i; ++j) { 358 for (j = 0; j < i; ++j) {
367 free(args[j]); 359 free(args[j]);
368 } 360 }
369 free(args); 361 free(args);
370 return NULL; 362 return NULL;
363 }
371 } 364 }
372 } 365 return args;
373 return args;
374} 366}
diff --git a/edify/expr.h b/edify/expr.h
index cfbef903..671b499b 100644
--- a/edify/expr.h
+++ b/edify/expr.h
@@ -17,45 +17,64 @@
17#ifndef _EXPRESSION_H 17#ifndef _EXPRESSION_H
18#define _EXPRESSION_H 18#define _EXPRESSION_H
19 19
20#include "yydefs.h"
21
20#define MAX_STRING_LEN 1024 22#define MAX_STRING_LEN 1024
21 23
22typedef struct Expr Expr; 24typedef struct Expr Expr;
23 25
24typedef char* (*Function)(const char* name, void* cookie, 26typedef struct {
27 // Optional pointer to app-specific data; the core of edify never
28 // uses this value.
29 void* cookie;
30
31 // The source of the original script. Must be NULL-terminated,
32 // and in writable memory (Evaluate may make temporary changes to
33 // it but will restore it when done).
34 char* script;
35
36 // The error message (if any) returned if the evaluation aborts.
37 // Should be NULL initially, will be either NULL or a malloc'd
38 // pointer after Evaluate() returns.
39 char* errmsg;
40} State;
41
42typedef char* (*Function)(const char* name, State* state,
25 int argc, Expr* argv[]); 43 int argc, Expr* argv[]);
26 44
27struct Expr { 45struct Expr {
28 Function fn; 46 Function fn;
29 char* name; 47 char* name;
30 int argc; 48 int argc;
31 Expr** argv; 49 Expr** argv;
50 int start, end;
32}; 51};
33 52
34char* Evaluate(void* cookie, Expr* expr); 53char* Evaluate(State* state, Expr* expr);
35 54
36// Glue to make an Expr out of a literal. 55// Glue to make an Expr out of a literal.
37char* Literal(const char* name, void* cookie, int argc, Expr* argv[]); 56char* Literal(const char* name, State* state, int argc, Expr* argv[]);
38 57
39// Functions corresponding to various syntactic sugar operators. 58// Functions corresponding to various syntactic sugar operators.
40// ("concat" is also available as a builtin function, to concatenate 59// ("concat" is also available as a builtin function, to concatenate
41// more than two strings.) 60// more than two strings.)
42char* ConcatFn(const char* name, void* cookie, int argc, Expr* argv[]); 61char* ConcatFn(const char* name, State* state, int argc, Expr* argv[]);
43char* LogicalAndFn(const char* name, void* cookie, int argc, Expr* argv[]); 62char* LogicalAndFn(const char* name, State* state, int argc, Expr* argv[]);
44char* LogicalOrFn(const char* name, void* cookie, int argc, Expr* argv[]); 63char* LogicalOrFn(const char* name, State* state, int argc, Expr* argv[]);
45char* LogicalNotFn(const char* name, void* cookie, int argc, Expr* argv[]); 64char* LogicalNotFn(const char* name, State* state, int argc, Expr* argv[]);
46char* SubstringFn(const char* name, void* cookie, int argc, Expr* argv[]); 65char* SubstringFn(const char* name, State* state, int argc, Expr* argv[]);
47char* EqualityFn(const char* name, void* cookie, int argc, Expr* argv[]); 66char* EqualityFn(const char* name, State* state, int argc, Expr* argv[]);
48char* InequalityFn(const char* name, void* cookie, int argc, Expr* argv[]); 67char* InequalityFn(const char* name, State* state, int argc, Expr* argv[]);
49char* SequenceFn(const char* name, void* cookie, int argc, Expr* argv[]); 68char* SequenceFn(const char* name, State* state, int argc, Expr* argv[]);
50 69
51// Convenience function for building expressions with a fixed number 70// Convenience function for building expressions with a fixed number
52// of arguments. 71// of arguments.
53Expr* Build(Function fn, int count, ...); 72Expr* Build(Function fn, YYLTYPE loc, int count, ...);
54 73
55// Global builtins, registered by RegisterBuiltins(). 74// Global builtins, registered by RegisterBuiltins().
56char* IfElseFn(const char* name, void* cookie, int argc, Expr* argv[]); 75char* IfElseFn(const char* name, State* state, int argc, Expr* argv[]);
57char* AssertFn(const char* name, void* cookie, int argc, Expr* argv[]); 76char* AssertFn(const char* name, State* state, int argc, Expr* argv[]);
58char* AbortFn(const char* name, void* cookie, int argc, Expr* argv[]); 77char* AbortFn(const char* name, State* state, int argc, Expr* argv[]);
59 78
60 79
61// For setting and getting the global error string (when returning 80// For setting and getting the global error string (when returning
@@ -91,13 +110,13 @@ Function FindFunction(const char* name);
91// Evaluate the expressions in argv, giving 'count' char* (the ... is 110// Evaluate the expressions in argv, giving 'count' char* (the ... is
92// zero or more char** to put them in). If any expression evaluates 111// zero or more char** to put them in). If any expression evaluates
93// to NULL, free the rest and return -1. Return 0 on success. 112// to NULL, free the rest and return -1. Return 0 on success.
94int ReadArgs(void* cookie, Expr* argv[], int count, ...); 113int ReadArgs(State* state, Expr* argv[], int count, ...);
95 114
96// Evaluate the expressions in argv, returning an array of char* 115// Evaluate the expressions in argv, returning an array of char*
97// results. If any evaluate to NULL, free the rest and return NULL. 116// results. If any evaluate to NULL, free the rest and return NULL.
98// The caller is responsible for freeing the returned array and the 117// The caller is responsible for freeing the returned array and the
99// strings it contains. 118// strings it contains.
100char** ReadVarArgs(void* cookie, int argc, Expr* argv[]); 119char** ReadVarArgs(State* state, int argc, Expr* argv[]);
101 120
102 121
103#endif // _EXPRESSION_H 122#endif // _EXPRESSION_H
diff --git a/edify/lexer.l b/edify/lexer.l
index cb5eb318..2c4489cc 100644
--- a/edify/lexer.l
+++ b/edify/lexer.l
@@ -16,14 +16,20 @@
16 */ 16 */
17 17
18#include "expr.h" 18#include "expr.h"
19#include "yydefs.h"
19#include "parser.h" 20#include "parser.h"
20 21
21int gLine = 1; 22int gLine = 1;
22int gColumn = 1; 23int gColumn = 1;
24int gPos = 0;
23 25
24// TODO: enforce MAX_STRING_LEN during lexing 26// TODO: enforce MAX_STRING_LEN during lexing
25char string_buffer[MAX_STRING_LEN]; 27char string_buffer[MAX_STRING_LEN];
26char* string_pos; 28char* string_pos;
29
30#define ADVANCE do {yylloc.start=gPos; yylloc.end=gPos+yyleng; \
31 gColumn+=yyleng; gPos+=yyleng;} while(0)
32
27%} 33%}
28 34
29%x STR 35%x STR
@@ -34,27 +40,32 @@ char* string_pos;
34 40
35 41
36\" { 42\" {
37 ++gColumn;
38 BEGIN(STR); 43 BEGIN(STR);
39 string_pos = string_buffer; 44 string_pos = string_buffer;
45 yylloc.start = gPos;
46 ++gColumn;
47 ++gPos;
40} 48}
41 49
42<STR>{ 50<STR>{
43 \" { 51 \" {
44 ++gColumn; 52 ++gColumn;
53 ++gPos;
45 BEGIN(INITIAL); 54 BEGIN(INITIAL);
46 *string_pos = '\0'; 55 *string_pos = '\0';
47 yylval.str = strdup(string_buffer); 56 yylval.str = strdup(string_buffer);
57 yylloc.end = gPos;
48 return STRING; 58 return STRING;
49 } 59 }
50 60
51 \\n { gColumn += yyleng; *string_pos++ = '\n'; } 61 \\n { gColumn += yyleng; gPos += yyleng; *string_pos++ = '\n'; }
52 \\t { gColumn += yyleng; *string_pos++ = '\t'; } 62 \\t { gColumn += yyleng; gPos += yyleng; *string_pos++ = '\t'; }
53 \\\" { gColumn += yyleng; *string_pos++ = '\"'; } 63 \\\" { gColumn += yyleng; gPos += yyleng; *string_pos++ = '\"'; }
54 \\\\ { gColumn += yyleng; *string_pos++ = '\\'; } 64 \\\\ { gColumn += yyleng; gPos += yyleng; *string_pos++ = '\\'; }
55 65
56 \\x[0-9a-fA-F]{2} { 66 \\x[0-9a-fA-F]{2} {
57 gColumn += yyleng; 67 gColumn += yyleng;
68 gPos += yyleng;
58 int val; 69 int val;
59 sscanf(yytext+2, "%x", &val); 70 sscanf(yytext+2, "%x", &val);
60 *string_pos++ = val; 71 *string_pos++ = val;
@@ -62,36 +73,38 @@ char* string_pos;
62 73
63 \n { 74 \n {
64 ++gLine; 75 ++gLine;
76 ++gPos;
65 gColumn = 1; 77 gColumn = 1;
66 *string_pos++ = yytext[0]; 78 *string_pos++ = yytext[0];
67 } 79 }
68 80
69 . { 81 . {
70 ++gColumn; 82 ++gColumn;
83 ++gPos;
71 *string_pos++ = yytext[0]; 84 *string_pos++ = yytext[0];
72 } 85 }
73} 86}
74 87
75if { gColumn += yyleng; return IF; } 88if ADVANCE; return IF;
76then { gColumn += yyleng; return THEN; } 89then ADVANCE; return THEN;
77else { gColumn += yyleng; return ELSE; } 90else ADVANCE; return ELSE;
78endif { gColumn += yyleng; return ENDIF; } 91endif ADVANCE; return ENDIF;
79 92
80[a-zA-Z0-9_:/.]+ { 93[a-zA-Z0-9_:/.]+ {
81 gColumn += yyleng; 94 ADVANCE;
82 yylval.str = strdup(yytext); 95 yylval.str = strdup(yytext);
83 return STRING; 96 return STRING;
84} 97}
85 98
86\&\& { gColumn += yyleng; return AND; } 99\&\& ADVANCE; return AND;
87\|\| { gColumn += yyleng; return OR; } 100\|\| ADVANCE; return OR;
88== { gColumn += yyleng; return EQ; } 101== ADVANCE; return EQ;
89!= { gColumn += yyleng; return NE; } 102!= ADVANCE; return NE;
90 103
91[+(),!;] { gColumn += yyleng; return yytext[0]; } 104[+(),!;] ADVANCE; return yytext[0];
92 105
93[ \t]+ gColumn += yyleng; 106[ \t]+ ADVANCE;
94 107
95(#.*)?\n { ++gLine; gColumn = 1; } 108(#.*)?\n gPos += yyleng; ++gLine; gColumn = 1;
96 109
97. return BAD; 110. return BAD;
diff --git a/edify/main.c b/edify/main.c
index 7da89e2e..03eefc69 100644
--- a/edify/main.c
+++ b/edify/main.c
@@ -21,152 +21,183 @@
21#include "expr.h" 21#include "expr.h"
22#include "parser.h" 22#include "parser.h"
23 23
24int expect(const char* expr_str, const char* expected, int* errors) { 24extern int yyparse(Expr** root, int* error_count);
25 Expr* e;
26 int error;
27 char* result;
28 25
29 printf("."); 26int expect(const char* expr_str, const char* expected, int* errors) {
27 Expr* e;
28 int error;
29 char* result;
30
31 printf(".");
32
33 yy_scan_string(expr_str);
34 int error_count = 0;
35 error = yyparse(&e, &error_count);
36 if (error > 0 || error_count > 0) {
37 fprintf(stderr, "error parsing \"%s\" (%d errors)\n",
38 expr_str, error_count);
39 ++*errors;
40 return 0;
41 }
30 42
31 yy_scan_string(expr_str); 43 State state;
32 error = yyparse(&e); 44 state.cookie = NULL;
33 if (error > 0) { 45 state.script = expr_str;
34 fprintf(stderr, "error parsing \"%s\"\n", expr_str); 46 state.errmsg = NULL;
35 ++*errors; 47
36 return 0; 48 result = Evaluate(&state, e);
37 } 49 free(state.errmsg);
50 if (result == NULL && expected != NULL) {
51 fprintf(stderr, "error evaluating \"%s\"\n", expr_str);
52 ++*errors;
53 return 0;
54 }
38 55
39 result = Evaluate(NULL, e); 56 if (result == NULL && expected == NULL) {
40 if (result == NULL && expected != NULL) { 57 return 1;
41 fprintf(stderr, "error evaluating \"%s\"\n", expr_str); 58 }
42 ++*errors;
43 return 0;
44 }
45 59
46 if (result == NULL && expected == NULL) { 60 if (strcmp(result, expected) != 0) {
47 return 1; 61 fprintf(stderr, "evaluating \"%s\": expected \"%s\", got \"%s\"\n",
48 } 62 expr_str, expected, result);
63 ++*errors;
64 free(result);
65 return 0;
66 }
49 67
50 if (strcmp(result, expected) != 0) {
51 fprintf(stderr, "evaluating \"%s\": expected \"%s\", got \"%s\"\n",
52 expr_str, expected, result);
53 ++*errors;
54 free(result); 68 free(result);
55 return 0; 69 return 1;
56 }
57
58 free(result);
59 return 1;
60} 70}
61 71
62int test() { 72int test() {
63 int errors = 0; 73 int errors = 0;
64 74
65 expect("a", "a", &errors); 75 expect("a", "a", &errors);
66 expect("\"a\"", "a", &errors); 76 expect("\"a\"", "a", &errors);
67 expect("\"\\x61\"", "a", &errors); 77 expect("\"\\x61\"", "a", &errors);
68 expect("# this is a comment\n" 78 expect("# this is a comment\n"
69 " a\n" 79 " a\n"
70 " \n", 80 " \n",
71 "a", &errors); 81 "a", &errors);
72 82
73 83
74 // sequence operator 84 // sequence operator
75 expect("a; b; c", "c", &errors); 85 expect("a; b; c", "c", &errors);
76 86
77 // string concat operator 87 // string concat operator
78 expect("a + b", "ab", &errors); 88 expect("a + b", "ab", &errors);
79 expect("a + \n \"b\"", "ab", &errors); 89 expect("a + \n \"b\"", "ab", &errors);
80 expect("a + b +\nc\n", "abc", &errors); 90 expect("a + b +\nc\n", "abc", &errors);
81 91
82 // string concat function 92 // string concat function
83 expect("concat(a, b)", "ab", &errors); 93 expect("concat(a, b)", "ab", &errors);
84 expect("concat(a,\n \"b\")", "ab", &errors); 94 expect("concat(a,\n \"b\")", "ab", &errors);
85 expect("concat(a + b,\nc,\"d\")", "abcd", &errors); 95 expect("concat(a + b,\nc,\"d\")", "abcd", &errors);
86 expect("\"concat\"(a + b,\nc,\"d\")", "abcd", &errors); 96 expect("\"concat\"(a + b,\nc,\"d\")", "abcd", &errors);
87 97
88 // logical and 98 // logical and
89 expect("a && b", "b", &errors); 99 expect("a && b", "b", &errors);
90 expect("a && \"\"", "", &errors); 100 expect("a && \"\"", "", &errors);
91 expect("\"\" && b", "", &errors); 101 expect("\"\" && b", "", &errors);
92 expect("\"\" && \"\"", "", &errors); 102 expect("\"\" && \"\"", "", &errors);
93 expect("\"\" && abort()", "", &errors); // test short-circuiting 103 expect("\"\" && abort()", "", &errors); // test short-circuiting
94 expect("t && abort()", NULL, &errors); 104 expect("t && abort()", NULL, &errors);
95 105
96 // logical or 106 // logical or
97 expect("a || b", "a", &errors); 107 expect("a || b", "a", &errors);
98 expect("a || \"\"", "a", &errors); 108 expect("a || \"\"", "a", &errors);
99 expect("\"\" || b", "b", &errors); 109 expect("\"\" || b", "b", &errors);
100 expect("\"\" || \"\"", "", &errors); 110 expect("\"\" || \"\"", "", &errors);
101 expect("a || abort()", "a", &errors); // test short-circuiting 111 expect("a || abort()", "a", &errors); // test short-circuiting
102 expect("\"\" || abort()", NULL, &errors); 112 expect("\"\" || abort()", NULL, &errors);
103 113
104 // logical not 114 // logical not
105 expect("!a", "", &errors); 115 expect("!a", "", &errors);
106 expect("! \"\"", "t", &errors); 116 expect("! \"\"", "t", &errors);
107 expect("!!a", "t", &errors); 117 expect("!!a", "t", &errors);
108 118
109 // precedence 119 // precedence
110 expect("\"\" == \"\" && b", "b", &errors); 120 expect("\"\" == \"\" && b", "b", &errors);
111 expect("a + b == ab", "t", &errors); 121 expect("a + b == ab", "t", &errors);
112 expect("ab == a + b", "t", &errors); 122 expect("ab == a + b", "t", &errors);
113 expect("a + (b == ab)", "a", &errors); 123 expect("a + (b == ab)", "a", &errors);
114 expect("(ab == a) + b", "b", &errors); 124 expect("(ab == a) + b", "b", &errors);
115 125
116 // substring function 126 // substring function
117 expect("is_substring(cad, abracadabra)", "t", &errors); 127 expect("is_substring(cad, abracadabra)", "t", &errors);
118 expect("is_substring(abrac, abracadabra)", "t", &errors); 128 expect("is_substring(abrac, abracadabra)", "t", &errors);
119 expect("is_substring(dabra, abracadabra)", "t", &errors); 129 expect("is_substring(dabra, abracadabra)", "t", &errors);
120 expect("is_substring(cad, abracxadabra)", "", &errors); 130 expect("is_substring(cad, abracxadabra)", "", &errors);
121 expect("is_substring(abrac, axbracadabra)", "", &errors); 131 expect("is_substring(abrac, axbracadabra)", "", &errors);
122 expect("is_substring(dabra, abracadabrxa)", "", &errors); 132 expect("is_substring(dabra, abracadabrxa)", "", &errors);
123 133
124 // ifelse function 134 // ifelse function
125 expect("ifelse(t, yes, no)", "yes", &errors); 135 expect("ifelse(t, yes, no)", "yes", &errors);
126 expect("ifelse(!t, yes, no)", "no", &errors); 136 expect("ifelse(!t, yes, no)", "no", &errors);
127 expect("ifelse(t, yes, abort())", "yes", &errors); 137 expect("ifelse(t, yes, abort())", "yes", &errors);
128 expect("ifelse(!t, abort(), no)", "no", &errors); 138 expect("ifelse(!t, abort(), no)", "no", &errors);
129 139
130 // if "statements" 140 // if "statements"
131 expect("if t then yes else no endif", "yes", &errors); 141 expect("if t then yes else no endif", "yes", &errors);
132 expect("if \"\" then yes else no endif", "no", &errors); 142 expect("if \"\" then yes else no endif", "no", &errors);
133 expect("if \"\" then yes endif", "", &errors); 143 expect("if \"\" then yes endif", "", &errors);
134 expect("if \"\"; t then yes endif", "yes", &errors); 144 expect("if \"\"; t then yes endif", "yes", &errors);
135 145
136 printf("\n"); 146 printf("\n");
137 147
138 return errors; 148 return errors;
149}
150
151void ExprDump(int depth, Expr* n, char* script) {
152 printf("%*s", depth*2, "");
153 char temp = script[n->end];
154 script[n->end] = '\0';
155 printf("%s %p (%d-%d) \"%s\"\n",
156 n->name == NULL ? "(NULL)" : n->name, n->fn, n->start, n->end,
157 script+n->start);
158 script[n->end] = temp;
159 int i;
160 for (i = 0; i < n->argc; ++i) {
161 ExprDump(depth+1, n->argv[i], script);
162 }
139} 163}
140 164
141int main(int argc, char** argv) { 165int main(int argc, char** argv) {
142 RegisterBuiltins(); 166 RegisterBuiltins();
143 FinishRegistration(); 167 FinishRegistration();
144 168
145 if (argc == 1) { 169 if (argc == 1) {
146 return test() != 0; 170 return test() != 0;
147 } 171 }
148 172
149 FILE* f = fopen(argv[1], "r"); 173 FILE* f = fopen(argv[1], "r");
150 char buffer[8192]; 174 char buffer[8192];
151 int size = fread(buffer, 1, 8191, f); 175 int size = fread(buffer, 1, 8191, f);
152 fclose(f); 176 fclose(f);
153 buffer[size] = '\0'; 177 buffer[size] = '\0';
154 178
155 Expr* root; 179 Expr* root;
156 int error_count = 0; 180 int error_count = 0;
157 yy_scan_bytes(buffer, size); 181 yy_scan_bytes(buffer, size);
158 int error = yyparse(&root, &error_count); 182 int error = yyparse(&root, &error_count);
159 printf("parse returned %d; %d errors encountered\n", error, error_count); 183 printf("parse returned %d; %d errors encountered\n", error, error_count);
160 if (error == 0 || error_count > 0) { 184 if (error == 0 || error_count > 0) {
161 char* result = Evaluate(NULL, root); 185
162 if (result == NULL) { 186 ExprDump(0, root, buffer);
163 char* errmsg = GetError(); 187
164 printf("result was NULL, message is: %s\n", 188 State state;
165 (errmsg == NULL ? "(NULL)" : errmsg)); 189 state.cookie = NULL;
166 ClearError(); 190 state.script = buffer;
167 } else { 191 state.errmsg = NULL;
168 printf("result is [%s]\n", result); 192
193 char* result = Evaluate(&state, root);
194 if (result == NULL) {
195 printf("result was NULL, message is: %s\n",
196 (state.errmsg == NULL ? "(NULL)" : state.errmsg));
197 free(state.errmsg);
198 } else {
199 printf("result is [%s]\n", result);
200 }
169 } 201 }
170 } 202 return 0;
171 return 0;
172} 203}
diff --git a/edify/parser.y b/edify/parser.y
index cf163c02..3f9ade14 100644
--- a/edify/parser.y
+++ b/edify/parser.y
@@ -20,6 +20,7 @@
20#include <string.h> 20#include <string.h>
21 21
22#include "expr.h" 22#include "expr.h"
23#include "yydefs.h"
23#include "parser.h" 24#include "parser.h"
24 25
25extern int gLine; 26extern int gLine;
@@ -30,6 +31,8 @@ int yyparse(Expr** root, int* error_count);
30 31
31%} 32%}
32 33
34%locations
35
33%union { 36%union {
34 char* str; 37 char* str;
35 Expr* expr; 38 Expr* expr;
@@ -68,19 +71,21 @@ expr: STRING {
68 $$->name = $1; 71 $$->name = $1;
69 $$->argc = 0; 72 $$->argc = 0;
70 $$->argv = NULL; 73 $$->argv = NULL;
74 $$->start = @$.start;
75 $$->end = @$.end;
71} 76}
72| '(' expr ')' { $$ = $2; } 77| '(' expr ')' { $$ = $2; $$->start=@$.start; $$->end=@$.end; }
73| expr ';' { $$ = $1; } 78| expr ';' { $$ = $1; $$->start=@1.start; $$->end=@1.end; }
74| expr ';' expr { $$ = Build(SequenceFn, 2, $1, $3); } 79| expr ';' expr { $$ = Build(SequenceFn, @$, 2, $1, $3); }
75| error ';' expr { $$ = $3; } 80| error ';' expr { $$ = $3; $$->start=@$.start; $$->end=@$.end; }
76| expr '+' expr { $$ = Build(ConcatFn, 2, $1, $3); } 81| expr '+' expr { $$ = Build(ConcatFn, @$, 2, $1, $3); }
77| expr EQ expr { $$ = Build(EqualityFn, 2, $1, $3); } 82| expr EQ expr { $$ = Build(EqualityFn, @$, 2, $1, $3); }
78| expr NE expr { $$ = Build(InequalityFn, 2, $1, $3); } 83| expr NE expr { $$ = Build(InequalityFn, @$, 2, $1, $3); }
79| expr AND expr { $$ = Build(LogicalAndFn, 2, $1, $3); } 84| expr AND expr { $$ = Build(LogicalAndFn, @$, 2, $1, $3); }
80| expr OR expr { $$ = Build(LogicalOrFn, 2, $1, $3); } 85| expr OR expr { $$ = Build(LogicalOrFn, @$, 2, $1, $3); }
81| '!' expr { $$ = Build(LogicalNotFn, 1, $2); } 86| '!' expr { $$ = Build(LogicalNotFn, @$, 1, $2); }
82| IF expr THEN expr ENDIF { $$ = Build(IfElseFn, 2, $2, $4); } 87| IF expr THEN expr ENDIF { $$ = Build(IfElseFn, @$, 2, $2, $4); }
83| IF expr THEN expr ELSE expr ENDIF { $$ = Build(IfElseFn, 3, $2, $4, $6); } 88| IF expr THEN expr ELSE expr ENDIF { $$ = Build(IfElseFn, @$, 3, $2, $4, $6); }
84| STRING '(' arglist ')' { 89| STRING '(' arglist ')' {
85 $$ = malloc(sizeof(Expr)); 90 $$ = malloc(sizeof(Expr));
86 $$->fn = FindFunction($1); 91 $$->fn = FindFunction($1);
@@ -93,6 +98,8 @@ expr: STRING {
93 $$->name = $1; 98 $$->name = $1;
94 $$->argc = $3.argc; 99 $$->argc = $3.argc;
95 $$->argv = $3.argv; 100 $$->argv = $3.argv;
101 $$->start = @$.start;
102 $$->end = @$.end;
96} 103}
97; 104;
98 105
diff --git a/edify/yydefs.h b/edify/yydefs.h
new file mode 100644
index 00000000..62578625
--- /dev/null
+++ b/edify/yydefs.h
@@ -0,0 +1,36 @@
1/*
2 * Copyright (C) 2009 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#ifndef _YYDEFS_H_
18#define _YYDEFS_H_
19
20#define YYLTYPE YYLTYPE
21typedef struct {
22 int start, end;
23} YYLTYPE;
24
25#define YYLLOC_DEFAULT(Current, Rhs, N) \
26 do { \
27 if (N) { \
28 (Current).start = YYRHSLOC(Rhs, 1).start; \
29 (Current).end = YYRHSLOC(Rhs, N).end; \
30 } else { \
31 (Current).start = YYRHSLOC(Rhs, 0).start; \
32 (Current).end = YYRHSLOC(Rhs, 0).end; \
33 } \
34 } while (0)
35
36#endif