1 //: Phase 1 of running Mu code: load it from a textual representation.
  2 //:
  3 //: The process of running Mu code:
  4 //:   load -> transform -> run
  5 
  6 :(scenarios load)  // use 'load' instead of 'run' in all scenarios in this layer
  7 :(scenario first_recipe)
  8 def main [
  9   1:number <- copy 23
 10 ]
 11 +parse: instruction: copy
 12 +parse:   ingredient: {23: "literal"}
 13 +parse:   product: {1: "number"}
 14 
 15 :(code)
 16 vector<recipe_ordinal> load(string form) {
 17   istringstream in(form);
 18   in >> std::noskipws;
 19   return load(in);
 20 }
 21 
 22 vector<recipe_ordinal> load(istream& in) {
 23   in >> std::noskipws;
 24   vector<recipe_ordinal> result;
 25   while (has_data(in)) {
 26     skip_whitespace_and_comments(in);
 27     if (!has_data(in)) break;
 28     string command = next_word(in);
 29     if (command.empty()) {
 30       assert(!has_data(in));
 31       break;
 32     }
 33     // Command Handlers
 34     if (command == "recipe" || command == "def") {
 35       recipe_ordinal r = slurp_recipe(in);
 36       if (r > 0) result.push_back(r);
 37     }
 38     else if (command == "recipe!" || command == "def!") {
 39       Disable_redefine_checks = true;
 40       recipe_ordinal r = slurp_recipe(in);
 41       if (r > 0) result.push_back(r);
 42       Disable_redefine_checks = false;
 43     }
 44     // End Command Handlers
 45     else {
 46       raise << "unknown top-level command: " << command << '\n' << end();
 47     }
 48   }
 49   return result;
 50 }
 51 
 52 // return the recipe ordinal slurped, or -1 if it failed
 53 int slurp_recipe(istream& in) {
 54   recipe result;
 55   result.name = next_word(in);
 56   if (result.name.empty()) {
 57     assert(!has_data(in));
 58     raise << "file ended with 'recipe'\n" << end();
 59     return -1;
 60   }
 61   // End Load Recipe Name
 62   skip_whitespace_but_not_newline(in);
 63   // End Recipe Refinements
 64   if (result.name.empty())
 65     raise << "empty result.name\n" << end();
 66   trace(9991, "parse") << "--- defining " << result.name << end();
 67   if (!contains_key(Recipe_ordinal, result.name))
 68     put(Recipe_ordinal, result.name, Next_recipe_ordinal);
 69   result.ordinal = get(Recipe_ordinal, result.name);
 70   ++Next_recipe_ordinal;
 71   if (Recipe.find(get(Recipe_ordinal, result.name)) != Recipe.end()) {
 72     trace(9991, &
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<html><head><title>Python: module ranger.__main__</title>
</head><body bgcolor="#f0f0f8">

<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
<tr bgcolor="#7799ee">
<td valign=bottom>&nbsp;<br>
<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong><a href="ranger.html"><font color="#ffffff">ranger</font></a>.__main__</strong></big></big></font></td
><td align=right valign=bottom
><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="file:/home/hut/ranger/ranger/__main__.py">/home/hut/ranger/ranger/__main__.py</a></font></td></tr></table>
    <p><tt>#&nbsp;coding=utf-8<br>
#<br>
#&nbsp;Copyright&nbsp;(C)&nbsp;2009,&nbsp;2010&nbsp;&nbsp;Roman&nbsp;Zimbelmann&nbsp;&lt;romanz@lavabit.com&gt;<br>
#<br>
#&nbsp;This&nbsp;program&nbsp;is&nbsp;free&nbsp;software:&nbsp;you&nbsp;can&nbsp;redistribute&nbsp;it&nbsp;and/or&nbsp;modify<br>
#&nbsp;it&nbsp;under&nbsp;the&nbsp;terms&nbsp;of&nbsp;the&nbsp;GNU&nbsp;General&nbsp;Public&nbsp;License&nbsp;as&nbsp;published&nbsp;by<br>
#&nbsp;the&nbsp;Free&nbsp;Software&nbsp;Foundation,&nbsp;either&nbsp;version&nbsp;3&nbsp;of&nbsp;the&nbsp;License,&nbsp;or<br>
#&nbsp;(at&nbsp;your&nbsp;option)&nbsp;any&nbsp;later&nbsp;version.<br>
#<br>
#&nbsp;This&nbsp;program&nbsp;is&nbsp;distributed&nbsp;in&nbsp;the&nbsp;hope&nbsp;that&nbsp;it&nbsp;will&nbsp;be&nbsp;useful,<br>
#&nbsp;but&nbsp;WITHOUT&nbsp;ANY&nbsp;WARRANTY;&nbsp;without&nbsp;even&nbsp;the&nbsp;implied&nbsp;warranty&nbsp;of<br>
#&nbsp;MERCHANTABILITY&nbsp;or&nbsp;FITNESS&nbsp;FOR&nbsp;A&nbsp;PARTICULAR&nbsp;PURPOSE.&nbsp;&nbsp;See&nbsp;the<br>
#&nbsp;GNU&nbsp;General&nbsp;Public&nbsp;License&nbsp;for&nbsp;more&nbsp;details.<br>
#<br>
#&nbsp;You&nbsp;should&nbsp;have&nbsp;received&nbsp;a&nbsp;copy&nbsp;of&nbsp;the&nbsp;GNU&nbsp;General&nbsp;Public&nbsp;License<br>
#&nbsp;along&nbsp;with&nbsp;this&nbsp;program.&nbsp;&nbsp;If&nbsp;not,&nbsp;see&nbsp;&lt;<a href="http://www.gnu.org/licenses/">http://www.gnu.org/licenses/</a>&gt;.</tt></p>
<p>
<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
<tr bgcolor="#aa55cc">
<td colspan=3 valign=bottom>&nbsp;<br>
<font color="#ffffff" face="helvetica, arial"><big><strong>Modules</strong></big></font></td></tr>
    
<tr><td bgcolor="#aa55cc"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
<td width="100%"><table width="100%" summary="list"><tr><td width="25%" valign=top><a href="os.html">os</a><br>
</td><td width="25%" valign=top><a href="sys.html">sys</a><br>
</td><td width="25%" valign=top></td><td width="25%" valign=top></td></tr></table></td></tr></table><p>
<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
<tr bgcolor="#eeaa77">
<td colspan=3 valign=bottom>&nbsp;<br>
<font color="#ffffff" face="helvetica, arial"><big><strong>Functions</strong></big></font></td></tr>
    
<tr><td bgcolor="#eeaa77"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
<td width="100%"><dl><dt><a name="-main"><strong>main</strong></a>()</dt><dd><tt>initialize&nbsp;objects&nbsp;and&nbsp;run&nbsp;the&nbsp;filemanager</tt></dd></dl>
 <dl><dt><a name="-parse_arguments"><strong>parse_arguments</strong></a>()</dt><dd><tt>Parse&nbsp;the&nbsp;program&nbsp;arguments</tt></dd></dl>
</td></tr></table>
</body></html>
lass="Delimiter">) && Terminators.find(in.peek()) != string::npos) { 198 in >> c; 199 out << c; 200 return; 201 } 202 while (in >> c) { 203 if (isspace(c) || Terminators.find(c) != string::npos || Ignore.find(c) != string::npos) { 204 in.putback(c); 205 break; 206 } 207 out << c; 208 } 209 } 210 211 void skip_whitespace_and_comments(istream& in) { 212 while (true) { 213 if (!has_data(in)) break; 214 if (isspace(in.peek())) in.get(); 215 else if (Ignore.find(in.peek()) != string::npos) in.get(); 216 else if (in.peek() == '#') skip_comment(in); 217 else break; 218 } 219 } 220 221 // confusing; move to the next line only to skip a comment, but never otherwise 222 void skip_whitespace_and_comments_but_not_newline(istream& in) { 223 while (true) { 224 if (!has_data(in)) break; 225 if (in.peek() == '\n') break; 226 if (isspace(in.peek())) in.get(); 227 else if (Ignore.find(in.peek()) != string::npos) in.get(); 228 else if (in.peek() == '#') skip_comment(in); 229 else break; 230 } 231 } 232 233 void skip_comment(istream& in) { 234 if (has_data(in) && in.peek() == '#') { 235 in.get(); 236 while (has_data(in) && in.peek() != '\n') in.get(); 237 } 238 } 239 240 :(scenario recipe_instead_of_def) 241 recipe main [ 242 1:number <- copy 23 243 ] 244 +parse: instruction: copy 245 +parse: ingredient: {23: "literal"} 246 +parse: product: {1: "number"} 247 248 :(scenario parse_comment_outside_recipe) 249 # this comment will be dropped by the tangler, so we need a dummy recipe to stop that 250 def f1 [ 251 ] 252 # this comment will go through to 'load' 253 def main [ 254 1:number <- copy 23 255 ] 256 +parse: instruction: copy 257 +parse: ingredient: {23: "literal"} 258 +parse: product: {1: "number"} 259 260 :(scenario parse_comment_amongst_instruction) 261 def main [ 262 # comment 263 1:number <- copy 23 264 ] 265 +parse: instruction: copy 266 +parse: ingredient: {23: "literal"} 267 +parse: product: {1: "number"} 268 269 :(scenario parse_comment_amongst_instruction_2) 270 def main [ 271 # comment 272 1:number <- copy 23 273 # comment 274 ] 275 +parse: instruction: copy 276 +parse: ingredient: {23: "literal"} 277 +parse: product: {1: "number"} 278 279 :(scenario parse_comment_amongst_instruction_3) 280 def main [ 281 1:number <- copy 23 282 # comment 283 2:number <- copy 23 284 ] 285 +parse: instruction: copy 286 +parse: ingredient: {23: "literal"} 287 +parse: product: {1: "number"} 288 +parse: instruction: copy 289 +parse: ingredient: {23: "literal"} 290 +parse: product: {2: "number"} 291 292 :(scenario parse_comment_after_instruction) 293 def main [ 294 1:number <- copy 23 # comment 295 ] 296 +parse: instruction: copy 297 +parse: ingredient: {23: "literal"} 298 +parse: product: {1: "number"} 299 300 :(scenario parse_label) 301 def main [ 302 +foo 303 ] 304 +parse: label: +foo 305 306 :(scenario parse_dollar_as_recipe_name) 307 def main [ 308 $foo 309 ] 310 +parse: instruction: $foo 311 312 :(scenario parse_multiple_properties) 313 def main [ 314 1:number <- copy 23/foo:bar:baz 315 ] 316 +parse: instruction: copy 317 +parse: ingredient: {23: "literal", "foo": ("bar" "baz")} 318 +parse: product: {1: "number"} 319 320 :(scenario parse_multiple_products) 321 def main [ 322 1:number, 2:number <- copy 23 323 ] 324 +parse: instruction: copy 325 +parse: ingredient: {23: "literal"} 326 +parse: product: {1: "number"} 327 +parse: product: {2: "number"} 328 329 :(scenario parse_multiple_ingredients) 330 def main [ 331 1:number, 2:number <- copy 23, 4:number 332 ] 333 +parse: instruction: copy 334 +parse: ingredient: {23: "literal"} 335 +parse: ingredient: {4: "number"} 336 +parse: product: {1: "number"} 337 +parse: product: {2: "number"} 338 339 :(scenario parse_multiple_types) 340 def main [ 341 1:number, 2:address:number <- copy 23, 4:number 342 ] 343 +parse: instruction: copy 344 +parse: ingredient: {23: "literal"} 345 +parse: ingredient: {4: "number"} 346 +parse: product: {1: "number"} 347 +parse: product: {2: ("address" "number")} 348 349 :(scenario parse_properties) 350 def main [ 351 1:address:number/lookup <- copy 23 352 ] 353 +parse: product: {1: ("address" "number"), "lookup": ()} 354 355 //: this test we can't represent with a scenario 356 :(code) 357 void test_parse_comment_terminated_by_eof() { 358 load("recipe main [\n" 359 " a:number <- copy 34\n" 360 "]\n" 361 "# abc"); // no newline after comment 362 cerr << "."; // termination = success 363 } 364 365 :(scenario warn_on_missing_space_before_bracket) 366 % Hide_errors = true; 367 def main[ 368 1:number <- copy 23 369 ] 370 +error: insert a space before '[' in 'main[' 371 372 //: Warn if a recipe gets redefined, because large codebases can accidentally 373 //: step on their own toes. But there'll be many occasions later where 374 //: we'll want to disable the errors. 375 :(before "End Globals") 376 bool Disable_redefine_checks = false; 377 :(before "End Reset") 378 Disable_redefine_checks = false; 379 :(code) 380 bool should_check_for_redefine(const string& recipe_name) { 381 if (Disable_redefine_checks) return false; 382 return true; 383 } 384 385 :(scenario forbid_redefining_recipes) 386 % Hide_errors = true; 387 def main [ 388 1:number <- copy 23 389 ] 390 def main [ 391 1:number <- copy 24 392 ] 393 +error: redefining recipe main 394 395 :(scenario permit_forcibly_redefining_recipes) 396 def main [ 397 1:number <- copy 23 398 ] 399 def! main [ 400 1:number <- copy 24 401 ] 402 -error: redefining recipe main 403 $error: 0 404 405 :(code) 406 // for debugging 407 void show_rest_of_stream(istream& in) { 408 cerr << '^'; 409 char c; 410 while (in >> c) 411 cerr << c; 412 cerr << "$\n"; 413 exit(0); 414 }