diff options
author | Doug Zongker | 2009-06-12 14:24:39 -0500 |
---|---|---|
committer | Doug Zongker | 2009-06-12 16:05:03 -0500 |
commit | d9c9d10d9da76f067d3955bea71f7bb39e859fa5 (patch) | |
tree | 1e49a3a616c3147f871e79b1b15e2b2a63379cc1 /edify | |
parent | 8edb00c990e563e6f91b278a212f2edf877cf763 (diff) | |
download | platform-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.c | 506 | ||||
-rw-r--r-- | edify/expr.h | 61 | ||||
-rw-r--r-- | edify/lexer.l | 47 | ||||
-rw-r--r-- | edify/main.c | 301 | ||||
-rw-r--r-- | edify/parser.y | 31 | ||||
-rw-r--r-- | edify/yydefs.h | 36 |
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 | ||
31 | int BooleanString(const char* s) { | 31 | int BooleanString(const char* s) { |
32 | return s[0] != '\0'; | 32 | return s[0] != '\0'; |
33 | } | 33 | } |
34 | 34 | ||
35 | char* Evaluate(void* cookie, Expr* expr) { | 35 | char* 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 | ||
39 | char* ConcatFn(const char* name, void* cookie, int argc, Expr* argv[]) { | 39 | char* 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 | |||
66 | done: | ||
67 | for (i = 0; i < argc; ++i) { | ||
68 | free(strings[i]); | ||
69 | } | ||
70 | return result; | ||
71 | } | 71 | } |
72 | 72 | ||
73 | char* IfElseFn(const char* name, void* cookie, int argc, Expr* argv[]) { | 73 | char* 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 | ||
95 | char* AbortFn(const char* name, void* cookie, int argc, Expr* argv[]) { | 95 | char* 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 | ||
105 | char* AssertFn(const char* name, void* cookie, int argc, Expr* argv[]) { | 109 | char* 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 | ||
122 | char* SleepFn(const char* name, void* cookie, int argc, Expr* argv[]) { | 134 | char* 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 | ||
132 | char* PrintFn(const char* name, void* cookie, int argc, Expr* argv[]) { | 144 | char* 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 | ||
145 | char* LogicalAndFn(const char* name, void* cookie, | 157 | char* 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 | ||
157 | char* LogicalOrFn(const char* name, void* cookie, | 169 | char* 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 | ||
169 | char* LogicalNotFn(const char* name, void* cookie, | 181 | char* 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 | ||
182 | char* SubstringFn(const char* name, void* cookie, | 194 | char* 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 | ||
198 | char* EqualityFn(const char* name, void* cookie, int argc, Expr* argv[]) { | 210 | char* 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 | ||
213 | char* 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 | ||
228 | char* SequenceFn(const char* name, void* cookie, int argc, Expr* argv[]) { | 225 | char* 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; | |
235 | char* Literal(const char* name, void* cookie, int argc, Expr* argv[]) { | 232 | } |
236 | return strdup(name); | ||
237 | } | ||
238 | 233 | ||
239 | Expr* 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 | // ----------------------------------------------------------------- | 240 | char* 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); | |
259 | static char* error_message = NULL; | 244 | return Evaluate(state, argv[1]); |
260 | |||
261 | void SetError(const char* message) { | ||
262 | if (error_message) { | ||
263 | free(error_message); | ||
264 | } | ||
265 | error_message = strdup(message); | ||
266 | } | 245 | } |
267 | 246 | ||
268 | const char* GetError() { | 247 | char* Literal(const char* name, State* state, int argc, Expr* argv[]) { |
269 | return error_message; | 248 | return strdup(name); |
270 | } | 249 | } |
271 | 250 | ||
272 | void ClearError() { | 251 | Expr* 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; | |||
283 | NamedFunction* fn_table = NULL; | 275 | NamedFunction* fn_table = NULL; |
284 | 276 | ||
285 | void RegisterFunction(const char* name, Function fn) { | 277 | void 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 | ||
295 | static int fn_entry_compare(const void* a, const void* b) { | 287 | static 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 | ||
301 | void FinishRegistration() { | 293 | void 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 | ||
305 | Function FindFunction(const char* name) { | 297 | Function 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 | ||
316 | void RegisterBuiltins() { | 308 | void 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. |
334 | int ReadArgs(void* cookie, Expr* argv[], int count, ...) { | 326 | int 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. |
359 | char** ReadVarArgs(void* cookie, int argc, Expr* argv[]) { | 351 | char** 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 | ||
22 | typedef struct Expr Expr; | 24 | typedef struct Expr Expr; |
23 | 25 | ||
24 | typedef char* (*Function)(const char* name, void* cookie, | 26 | typedef 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 | |||
42 | typedef char* (*Function)(const char* name, State* state, | ||
25 | int argc, Expr* argv[]); | 43 | int argc, Expr* argv[]); |
26 | 44 | ||
27 | struct Expr { | 45 | struct 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 | ||
34 | char* Evaluate(void* cookie, Expr* expr); | 53 | char* 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. |
37 | char* Literal(const char* name, void* cookie, int argc, Expr* argv[]); | 56 | char* 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.) |
42 | char* ConcatFn(const char* name, void* cookie, int argc, Expr* argv[]); | 61 | char* ConcatFn(const char* name, State* state, int argc, Expr* argv[]); |
43 | char* LogicalAndFn(const char* name, void* cookie, int argc, Expr* argv[]); | 62 | char* LogicalAndFn(const char* name, State* state, int argc, Expr* argv[]); |
44 | char* LogicalOrFn(const char* name, void* cookie, int argc, Expr* argv[]); | 63 | char* LogicalOrFn(const char* name, State* state, int argc, Expr* argv[]); |
45 | char* LogicalNotFn(const char* name, void* cookie, int argc, Expr* argv[]); | 64 | char* LogicalNotFn(const char* name, State* state, int argc, Expr* argv[]); |
46 | char* SubstringFn(const char* name, void* cookie, int argc, Expr* argv[]); | 65 | char* SubstringFn(const char* name, State* state, int argc, Expr* argv[]); |
47 | char* EqualityFn(const char* name, void* cookie, int argc, Expr* argv[]); | 66 | char* EqualityFn(const char* name, State* state, int argc, Expr* argv[]); |
48 | char* InequalityFn(const char* name, void* cookie, int argc, Expr* argv[]); | 67 | char* InequalityFn(const char* name, State* state, int argc, Expr* argv[]); |
49 | char* SequenceFn(const char* name, void* cookie, int argc, Expr* argv[]); | 68 | char* 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. |
53 | Expr* Build(Function fn, int count, ...); | 72 | Expr* Build(Function fn, YYLTYPE loc, int count, ...); |
54 | 73 | ||
55 | // Global builtins, registered by RegisterBuiltins(). | 74 | // Global builtins, registered by RegisterBuiltins(). |
56 | char* IfElseFn(const char* name, void* cookie, int argc, Expr* argv[]); | 75 | char* IfElseFn(const char* name, State* state, int argc, Expr* argv[]); |
57 | char* AssertFn(const char* name, void* cookie, int argc, Expr* argv[]); | 76 | char* AssertFn(const char* name, State* state, int argc, Expr* argv[]); |
58 | char* AbortFn(const char* name, void* cookie, int argc, Expr* argv[]); | 77 | char* 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. |
94 | int ReadArgs(void* cookie, Expr* argv[], int count, ...); | 113 | int 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. |
100 | char** ReadVarArgs(void* cookie, int argc, Expr* argv[]); | 119 | char** 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 | ||
21 | int gLine = 1; | 22 | int gLine = 1; |
22 | int gColumn = 1; | 23 | int gColumn = 1; |
24 | int gPos = 0; | ||
23 | 25 | ||
24 | // TODO: enforce MAX_STRING_LEN during lexing | 26 | // TODO: enforce MAX_STRING_LEN during lexing |
25 | char string_buffer[MAX_STRING_LEN]; | 27 | char string_buffer[MAX_STRING_LEN]; |
26 | char* string_pos; | 28 | char* 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 | ||
75 | if { gColumn += yyleng; return IF; } | 88 | if ADVANCE; return IF; |
76 | then { gColumn += yyleng; return THEN; } | 89 | then ADVANCE; return THEN; |
77 | else { gColumn += yyleng; return ELSE; } | 90 | else ADVANCE; return ELSE; |
78 | endif { gColumn += yyleng; return ENDIF; } | 91 | endif 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 | ||
24 | int expect(const char* expr_str, const char* expected, int* errors) { | 24 | extern int yyparse(Expr** root, int* error_count); |
25 | Expr* e; | ||
26 | int error; | ||
27 | char* result; | ||
28 | 25 | ||
29 | printf("."); | 26 | int 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 | ||
62 | int test() { | 72 | int 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 | |||
151 | void 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 | ||
141 | int main(int argc, char** argv) { | 165 | int 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 | ||
25 | extern int gLine; | 26 | extern 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 | ||
21 | typedef 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 | ||