<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
<title>Mu - 050scenario.cc</title>
<meta name="Generator" content="Vim/7.4">
<meta name="plugin-version" content="vim7.4_v2">
<meta name="syntax" content="cpp">
<meta name="settings" content="use_css,pre_wrap,no_foldcolumn,expand_tabs,prevent_copy=">
<meta name="colorscheme" content="minimal">
<style type="text/css">
<!--
pre { white-space: pre-wrap; font-family: monospace; color: #eeeeee; background-color: #080808; }
body { font-size: 12pt; font-family: monospace; color: #eeeeee; background-color: #080808; }
* { font-size: 12pt; font-size: 1em; }
.Constant { color: #00a0a0; }
.traceAbsent { color: #c00000; }
.cSpecial { color: #008000; }
.traceContains { color: #008000; }
.SalientComment { color: #00ffff; }
.Comment { color: #9090ff; }
.Delimiter { color: #800080; }
.Special { color: #c00000; }
.Identifier { color: #fcb165; }
.Normal { color: #eeeeee; background-color: #080808; padding-bottom: 1px; }
.CommentedCode { color: #6c6c6c; }
-->
</style>
<script type='text/javascript'>
<!--
-->
</script>
</head>
<body>
<pre id='vimCodeElement'>
<span class="Comment">//: Mu scenarios. This will get long, but these are the tests we want to</span>
<span class="Comment">//: support in this layer.</span>
<span class="Comment">//: You can use variable names in scenarios, but for the most part we'll use</span>
<span class="Comment">//: raw location numbers, because that lets us make assertions on memory.</span>
<span class="Comment">//: Tests should avoid abstraction as far as possible.</span>
<span class="Delimiter">:(scenarios run_mu_scenario)</span>
<span class="Delimiter">:(scenario scenario_block)</span>
scenario foo [
run [
<span class="Constant">1</span>:number<span class="Special"> <- </span>copy <span class="Constant">13</span>
]
memory-should-contain [
<span class="Constant">1</span><span class="Special"> <- </span><span class="Constant">13</span>
]
]
<span class="Comment"># checks are inside scenario</span>
<span class="Delimiter">:(scenario scenario_multiple_blocks)</span>
scenario foo [
run [
<span class="Constant">1</span>:number<span class="Special"> <- </span>copy <span class="Constant">13</span>
]
memory-should-contain [
<span class="Constant">1</span><span class="Special"> <- </span><span class="Constant">13</span>
]
run [
<span class="Constant">2</span>:number<span class="Special"> <- </span>copy <span class="Constant">13</span>
]
memory-should-contain [
<span class="Constant">1</span><span class="Special"> <- </span><span class="Constant">13</span>
<span class="Constant">2</span><span class="Special"> <- </span><span class="Constant">13</span>
]
]
<span class="Delimiter">:(scenario scenario_check_memory_and_trace)</span>
scenario foo [
run [
<span class="Constant">1</span>:number<span class="Special"> <- </span>copy <span class="Constant">13</span>
trace <span class="Constant">1</span><span class="Delimiter">,</span> [a]<span class="Delimiter">,</span> [a b c]
]
memory-should-contain [
<span class="Constant">1</span><span class="Special"> <- </span><span class="Constant">13</span>
]
trace-should-contain [
<span class="Normal">a</span>: a b c
]
trace-should-<span class="Normal">not</span>-contain [
<span class="Normal">a</span>: x y z
]
]
<span class="SalientComment">//:: Core data structure</span>
<span class="Delimiter">:(before "End Types")</span>
<span class="Normal">struct</span> scenario <span class="Delimiter">{</span>
string name<span class="Delimiter">;</span>
string to_run<span class="Delimiter">;</span>
<span class="Delimiter">};</span>
<span class="Delimiter">:(before "End Globals")</span>
vector<scenario> Scenarios<span class="Delimiter">;</span>
set<string> Scenario_names<span class="Delimiter">;</span>
<span class="SalientComment">//:: Parse the 'scenario' form.</span>
<span class="Comment">//: Simply store the text of the scenario.</span>
<span class="Delimiter">:(before "End Command Handlers")</span>
<span class="Normal">else</span> <span class="Normal">if</span> <span class="Delimiter">(</span>command == <span class="Constant">"scenario"</span><span class="Delimiter">)</span> <span class="Delimiter">{</span>
Scenarios<span class="Delimiter">.</span>push_back<span class="Delimiter">(</span>parse_scenario<span class="Delimiter">(</span>in<span class="Delimiter">));</span>
<span class="Delimiter">}</span>
<span class="Delimiter">:(code)</span>
scenario parse_scenario<span class="Delimiter">(</span>istream& in<span class="Delimiter">)</span> <span class="Delimiter">{</span>
scenario result<span class="Delimiter">;</span>
result<span class="Delimiter">.</span>name = next_word<span class="Delimiter">(</span>in<span class="Delimiter">);</span>
<span class="Normal">if</span> <span class="Delimiter">(</span>contains_key<span class="Delimiter">(</span>Scenario_names<span class="Delimiter">,</span> result<span class="Delimiter">.</span>name<span class="Delimiter">))</span>
raise << <span class="Constant">"duplicate scenario name: '"</span> << result<span class="Delimiter">.</span>name << <span class="Constant">"'</span><span class="cSpecial">\n</span><span class="Constant">"</span> << end<span class="Delimiter">();</span>
Scenario_names<span class="Delimiter">.</span>insert<span class="Delimiter">(</span>result<span class="Delimiter">.</span>name<span class="Delimiter">);</span>
skip_whitespace_and_comments<span class="Delimiter">(</span>in<span class="Delimiter">);</span>
<span class="Normal">if</span> <span class="Delimiter">(</span>in<span class="Delimiter">.</span>peek<span class="Delimiter">()</span> != <span class="Constant">'['</span><span class="Delimiter">)</span> <span class="Delimiter">{</span>
raise << <span class="Constant">"Expected '[' after scenario '"</span> << result<span class="Delimiter">.</span>name << <span class="Constant">"'</span><span class="cSpecial">\n</span><span class="Constant">"</span> << end<span class="Delimiter">();</span>
exit<span class="Delimiter">(</span><span class="Constant">0</span><span class="Delimiter">);</span>
<span class="Delimiter">}</span>
<span class="Comment">// scenarios are take special 'code' strings so we need to ignore brackets</span>
<span class="Comment">// inside comments</span>
result<span class="Delimiter">.</span>to_run = slurp_quoted<span class="Delimiter">(</span>in<span class="Delimiter">);</span>
<span class="Comment">// delete [] delimiters</span>
assert<span class="Delimiter">(</span>result<span class="Delimiter">.</span>to_run<span class="Delimiter">.</span>at<span class="Delimiter">(</span><span class="Constant">0</span><span class="Delimiter">)</span> == <span class="Constant">'['</span><span class="Delimiter">);</span>
result<span class="Delimiter">.</span>to_run<span class="Delimiter">.</span>erase<span class="Delimiter">(</span><span class="Constant">0</span><span class="Delimiter">,</span> <span class="Constant">1</span><span class="Delimiter">);</span>
assert<span class="Delimiter">(</span>result<span class="Delimiter">.</span>to_run<span class="Delimiter">.</span>at<span class="Delimiter">(</span>SIZE<span class="Delimiter">(</span>result<span class="Delimiter">.</span>to_run<span class="Delimiter">)</span>-<span class="Constant">1</span><span class="Delimiter">)</span> == <span class="Constant">']'</span><span class="Delimiter">);</span>
result<span class="Delimiter">.</span>to_run<span class="Delimiter">.</span>erase<span class="Delimiter">(</span>SIZE<span class="Delimiter">(</span>result<span class="Delimiter">.</span>to_run<span class="Delimiter">)</span>-<span class="Constant">1</span><span class="Delimiter">);</span>
<span class="Identifier">return</span> result<span class="Delimiter">;</span>
<span class="Delimiter">}</span>
<span class="Delimiter">:(scenario read_scenario_with_bracket_in_comment)</span>
scenario foo [
<span class="Comment"># ']' in comment</span>
<span class="Constant">1</span>:number<span class="Special"> <- </span>copy <span class="Constant">0</span>
]
<span class="traceContains">+run: {1: "number"} <- copy {0: "literal"}</span>
<span class="Delimiter">:(scenario read_scenario_with_bracket_in_comment_in_nested_string)</span>
scenario foo [
<span class="Constant">1</span>:address:array:character<span class="Special"> <- </span><span class="Normal">new</span> [<span class="Comment"># not a comment]</span>
]
<span class="traceContains">+run: {1: ("address" "array" "character")} <- new {"# not a comment": "literal-string"}</span>
<span class="SalientComment">//:: Run scenarios when we run 'mu test'.</span>
<span class="Comment">//: Treat the text of the scenario as a regular series of instructions.</span>
<span class="Delimiter">:(before "End Globals")</span>
<span class="Normal">int</span> Num_core_mu_tests = <span class="Constant">0</span><span class="Delimiter">;</span>
<span class="Delimiter">:(after "Check For .mu Files")</span>
Num_core_mu_tests = SIZE<span class="Delimiter">(</span>Scenarios<span class="Delimiter">);</span>
<span class="Delimiter">:(before "End Tests")</span>
Hide_missing_default_space_errors = <span class="Constant">false</span><span class="Delimiter">;</span>
<span class="Normal">time_t</span> mu_time<span class="Delimiter">;</span> time<span class="Delimiter">(</span>&mu_time<span class="Delimiter">);</span>
cerr << <span class="Constant">"</span><span class="cSpecial">\n</span><span class="Constant">Mu tests: "</span> << ctime<span class="Delimiter">(</span>&mu_time<span class="Delimiter">);</span>
<span class="Normal">for</span> <span class="Delimiter">(</span><span class="Normal">int</span> i = <span class="Constant">0</span><span class="Delimiter">;</span> i < SIZE<span class="Delimiter">(</span>Scenarios<span class="Delimiter">);</span> ++i<span class="Delimiter">)</span> <span class="Delimiter">{</span>
<span class="CommentedCode">//? cerr << i << ": " << Scenarios.at(i).name << '\n';</span>
<span class="Normal">if</span> <span class="Delimiter">(</span>i == Num_core_mu_tests<span class="Delimiter">)</span> <span class="Delimiter">{</span>
time<span class="Delimiter">(</span>&t<span class="Delimiter">);</span>
cerr << <span class="Constant">"</span><span class="cSpecial">\n</span><span class="Constant">App tests: "</span> << ctime<span class="Delimiter">(</span>&t<span class="Delimiter">);</span>
<span class="Delimiter">}</span>
run_mu_scenario<span class="Delimiter">(</span>Scenarios<span class="Delimiter">.</span>at<span class="Delimiter">(</span>i<span class="Delimiter">));</span>
<span class="Normal">if</span> <span class="Delimiter">(</span>Passed<span class="Delimiter">)</span> cerr << <span class="Constant">"."</span><span class="Delimiter">;</span>
<span class="Delimiter">}</span>
<span class="Comment">//: Convenience: run a single named scenario.</span>
<span class="Delimiter">:(after "Test Runs")</span>
<span class="Normal">for</span> <span class="Delimiter">(</span><span class="Normal">int</span> i = <span class="Constant">0</span><span class="Delimiter">;</span> i < SIZE<span class="Delimiter">(</span>Scenarios<span class="Delimiter">);</span> ++i<span class="Delimiter">)</span> <span class="Delimiter">{</span>
<span class="Normal">if</span> <span class="Delimiter">(</span>Scenarios<span class="Delimiter">.</span>at<span class="Delimiter">(</span>i<span class="Delimiter">).</span>name == argv[argc-<span class="Constant">1</span>]<span class="Delimiter">)</span> <span class="Delimiter">{</span>
run_mu_scenario<span class="Delimiter">(</span>Scenarios<span class="Delimiter">.</span>at<span class="Delimiter">(</span>i<span class="Delimiter">));</span>
<span class="Normal">if</span> <span class="Delimiter">(</span>Passed<span class="Delimiter">)</span> cerr << <span class="Constant">".</span><span class="cSpecial">\n</span><span class="Constant">"</span><span class="Delimiter">;</span>
<span class="Identifier">return</span> <span class="Constant">0</span><span class="Delimiter">;</span>
<span class="Delimiter">}</span>
<span class="Delimiter">}</span>
<span class="Delimiter">:(before "End Globals")</span>
<span class="Normal">const</span> scenario* Current_scenario = <span class="Constant">NULL</span><span class="Delimiter">;</span>
<span class="Delimiter">:(code)</span>
<span class="Normal">void</span> run_mu_scenario<span class="Delimiter">(</span><span class="Normal">const</span> scenario& s<span class="Delimiter">)</span> <span class="Delimiter">{</span>
Current_scenario = &s<span class="Delimiter">;</span>
<span class="Normal">bool</span> not_already_inside_test = !Trace_stream<span class="Delimiter">;</span>
<span class="CommentedCode">//? cerr << s.name << '\n';</span>
<span class="Normal">if</span> <span class="Delimiter">(</span>not_already_inside_test<span class="Delimiter">)</span> <span class="Delimiter">{</span>
Trace_stream = <span class="Normal">new</span> trace_stream<span class="Delimiter">;</span>
setup<span class="Delimiter">();</span>
<span class="Delimiter">}</span>
vector<recipe_ordinal> tmp = load<span class="Delimiter">(</span><span class="Constant">"recipe scenario_"</span>+s<span class="Delimiter">.</span>name+<span class="Constant">" [ "</span>+s<span class="Delimiter">.</span>to_run+<span class="Constant">" ]"</span><span class="Delimiter">);</span>
mark_autogenerated<span class="Delimiter">(</span>tmp<span class="Delimiter">.</span>at<span class="Delimiter">(</span><span class="Constant">0</span><span class="Delimiter">));</span>
bind_special_scenario_names<span class="Delimiter">(</span>tmp<span class="Delimiter">.</span>at<span class="Delimiter">(</span><span class="Constant">0</span><span class="Delimiter">));</span>
transform_all<span class="Delimiter">();</span>
run<span class="Delimiter">(</span>tmp<span class="Delimiter">.</span>front<span class="Delimiter">());</span>
<span class="Normal">if</span> <span class="Delimiter">(</span>Passed && !Hide_errors && trace_count<span class="Delimiter">(</span><span class="Constant">"error"</span><span class="Delimiter">)</span> > <span class="Constant">0</span><span class="Delimiter">)</span> <span class="Delimiter">{</span>
Passed = <span class="Constant">false</span><span class="Delimiter">;</span>
++Num_failures<span class="Delimiter">;</span>
<span class="Delimiter">}</span>
<span class="Comment">// End Mu Test Teardown</span>
<span class="Normal">if</span> <span class="Delimiter">(</span>not_already_inside_test && Trace_stream<span class="Delimiter">)</span> <span class="Delimiter">{</span>
teardown<span class="Delimiter">();</span>
<span class="Normal">if</span> <span class="Delimiter">(</span>Save_trace<span class="Delimiter">)</span> <span class="Delimiter">{</span>
ofstream fout<span class="Delimiter">(</span><span class="Constant">"last_trace"</span><span class="Delimiter">);</span>
fout << Trace_stream<span class="Delimiter">-></span>readable_contents<span class="Delimiter">(</span><span class="Constant">""</span><span class="Delimiter">);</span>
fout<span class="Delimiter">.</span>close<span class="Delimiter">();</span>
<span class="Delimiter">}</span>
<span class="Normal">delete</span> Trace_stream<span class="Delimiter">;</span>
Trace_stream = <span class="Constant">NULL</span><span class="Delimiter">;</span>
<span class="Delimiter">}</span>
Current_scenario = <span class="Constant">NULL</span><span class="Delimiter">;</span>
<span class="Delimiter">}</span>
<span class="Comment">//: Some variables for fake resources always get special /raw addresses in scenarios.</span>
<span class="Comment">// Should contain everything passed by is_special_name but failed by is_disqualified.</span>
<span class="Normal">void</span> bind_special_scenario_names<span class="Delimiter">(</span>recipe_ordinal r<span class="Delimiter">)</span> <span class="Delimiter">{</span>
<span class="Comment">// Special Scenario Variable Names(r)</span>
<span class="Comment">// End Special Scenario Variable Names(r)</span>
<span class="Delimiter">}</span>
<span class="Delimiter">:(before "Done Placing Ingredient(inst, in, caller)")</span>
maybe_make_raw<span class="Delimiter">(</span>inst<span class="Delimiter">.</span>ingredients<span class="Delimiter">.</span>at<span class="Delimiter">(</span>in<span class="Delimiter">),</span> caller<span class="Delimiter">);</span>
<span class="Delimiter">:(before "Done Placing Product(inst, out, caller)")</span>
maybe_make_raw<span class="Delimiter">(</span>inst<span class="Delimiter">.</span>products<span class="Delimiter">.</span>at<span class="Delimiter">(</span>out<span class="Delimiter">),</span> caller<span class="Delimiter">);</span>
<span class="Delimiter">:(code)</span>
<span class="Normal">void</span> maybe_make_raw<span class="Delimiter">(</span>reagent& r<span class="Delimiter">,</span> <span class="Normal">const</span> recipe& caller<span class="Delimiter">)</span> <span class="Delimiter">{</span>
<span class="Normal">if</span> <span class="Delimiter">(</span>!is_special_name<span class="Delimiter">(</span>r<span class="Delimiter">.</span>name<span class="Delimiter">))</span> <span class="Identifier">return</span><span class="Delimiter">;</span>
<span class="Normal">if</span> <span class="Delimiter">(</span>starts_with<span class="Delimiter">(</span>caller<span class="Delimiter">.</span>name<span class="Delimiter">,</span> <span class="Constant">"scenario_"</span><span class="Delimiter">))</span>
r<span class="Delimiter">.</span>properties<span class="Delimiter">.</span>push_back<span class="Delimiter">(</span>pair<string<span class="Delimiter">,</span> string_tree*><span class="Delimiter">(</span><span class="Constant">"raw"</span><span class="Delimiter">,</span> <span class="Constant">NULL</span><span class="Delimiter">));</span>
<span class="Comment">// End maybe_make_raw</span>
<span class="Delimiter">}</span>
<span class="Comment">//: Test.</span>
<span class="Delimiter">:(before "End is_special_name Cases")</span>
<span class="Normal">if</span> <span class="Delimiter">(</span>s == <span class="Constant">"__maybe_make_raw_test__"</span><span class="Delimiter">)</span> <span class="Identifier">return</span> <span class="Constant">true</span><span class="Delimiter">;</span>
<span class="Delimiter">:(before "End Special Scenario Variable Names(r)")</span>
<span class="Comment">//: ugly: we only need this for this one test, but need to define it for all time</span>
Name[r][<span class="Constant">"__maybe_make_raw_test__"</span>] = Reserved_for_tests-<span class="Constant">1</span><span class="Delimiter">;</span>
<span class="Delimiter">:(code)</span>
<span class="Normal">void</span> test_maybe_make_raw<span class="Delimiter">()</span> <span class="Delimiter">{</span>
<span class="Comment">// check that scenarios can use local-scope and special variables together</span>
vector<recipe_ordinal> tmp = load<span class="Delimiter">(</span>
<span class="Constant">"def scenario_foo [</span><span class="cSpecial">\n</span><span class="Constant">"</span>
<span class="Constant">" local-scope</span><span class="cSpecial">\n</span><span class="Constant">"</span>
<span class="Constant">" __maybe_make_raw_test__:number <- copy 34</span><span class="cSpecial">\n</span><span class="Constant">"</span>
<span class="Constant">"]</span><span class="cSpecial">\n</span><span class="Constant">"</span><span class="Delimiter">);</span>
mark_autogenerated<span class="Delimiter">(</span>tmp<span class="Delimiter">.</span>at<span class="Delimiter">(</span><span class="Constant">0</span><span class="Delimiter">));</span>
bind_special_scenario_names<span class="Delimiter">(</span>tmp<span class="Delimiter">.</span>at<span class="Delimiter">(</span><span class="Constant">0</span><span class="Delimiter">));</span>
transform_all<span class="Delimiter">();</span>
run<span class="Delimiter">(</span>tmp<span class="Delimiter">.</span>at<span class="Delimiter">(</span><span class="Constant">0</span><span class="Delimiter">));</span>
CHECK<span class="Delimiter">(</span>trace_count<span class="Delimiter">(</span><span class="Constant">"error"</span><span class="Delimiter">)</span> == <span class="Constant">0</span><span class="Delimiter">);</span>
<span class="Delimiter">}</span>
<span class="Comment">//: Watch out for redefinitions of scenario routines. We should never ever be</span>
<span class="Comment">//: doing that, regardless of anything else.</span>
<span class="Delimiter">:(scenarios run)</span>
<span class="Delimiter">:(scenario forbid_redefining_scenario_even_if_forced)</span>
<span class="Special">% Hide_errors = true;</span>
<span class="Special">% Disable_redefine_checks = true;</span>
def scenario-foo [
<span class="Constant">1</span>:number<span class="Special"> <- </span>copy <span class="Constant">34</span>
]
def scenario-foo [
<span class="Constant">1</span>:number<span class="Special"> <- </span>copy <span class="Constant">35</span>
]
<span class="traceContains">+error: redefining recipe scenario-foo</span>
<span class="Delimiter">:(after "bool should_check_for_redefine(const string& recipe_name)")</span>
<span class="Normal">if</span> <span class="Delimiter">(</span>recipe_name<span class="Delimiter">.</span>find<span class="Delimiter">(</span><span class="Constant">"scenario-"</span><span class="Delimiter">)</span> == <span class="Constant">0</span><span class="Delimiter">)</span> <span class="Identifier">return</span> <span class="Constant">true</span><span class="Delimiter">;</span>
<span class="SalientComment">//:: The special instructions we want to support inside scenarios.</span>
<span class="Comment">//: In a compiler for the mu VM these will require more work.</span>
<span class="Comment">//: 'run' interprets a string as a set of instructions</span>
<span class="Delimiter">:(scenario run)</span>
def main [
run [
<span class="Constant">1</span>:number<span class="Special"> <- </span>copy <span class="Constant">13</span>
]
]
<span class="traceContains">+mem: storing 13 in location 1</span>
<span class="Delimiter">:(before "End Primitive Recipe Declarations")</span>
RUN<span class="Delimiter">,</span>
<span class="Delimiter">:(before "End Primitive Recipe Numbers")</span>
put<span class="Delimiter">(</span>Recipe_ordinal<span class="Delimiter">,</span> <span class="Constant">"run"</span><span class="Delimiter">,</span> RUN<span class="Delimiter">);</span>
<span class="Delimiter">:(before "End Primitive Recipe Checks")</span>
<span class="Normal">case</span> RUN: <span class="Delimiter">{</span>
<span class="Identifier">break</span><span class="Delimiter">;</span>
<span class="Delimiter">}</span>
<span class="Delimiter">:(before "End Primitive Recipe Implementations")</span>
<span class="Normal">case</span> RUN: <span class="Delimiter">{</span>
assert<span class="Delimiter">(</span>Name[Next_recipe_ordinal]<span class="Delimiter">.</span>empty<span class="Delimiter">());</span>
ostringstream tmp<span class="Delimiter">;</span>
tmp << <span class="Constant">"recipe run_"</span> << Next_recipe_ordinal << <span class="Constant">" [ "</span> << current_instruction<span class="Delimiter">().</span>ingredients<span class="Delimiter">.</span>at<span class="Delimiter">(</span><span class="Constant">0</span><span class="Delimiter">).</span>name << <span class="Constant">" ]"</span><span class="Delimiter">;</span>
vector<recipe_ordinal> tmp_recipe = load<span class="Delimiter">(</span>tmp<span class="Delimiter">.</span>str<span class="Delimiter">());</span>
mark_autogenerated<span class="Delimiter">(</span>tmp_recipe<span class="Delimiter">.</span>at<span class="Delimiter">(</span><span class="Constant">0</span><span class="Delimiter">));</span>
bind_special_scenario_names<span class="Delimiter">(</span>tmp_recipe<span class="Delimiter">.</span>at<span class="Delimiter">(</span><span class="Constant">0</span><span class="Delimiter">));</span>
transform_all<span class="Delimiter">();</span>
<span class="Normal">if</span> <span class="Delimiter">(</span>Trace_stream<span class="Delimiter">)</span> <span class="Delimiter">{</span>
++Trace_stream<span class="Delimiter">-></span>callstack_depth<span class="Delimiter">;</span>
trace<span class="Delimiter">(</span><span class="Constant">9998</span><span class="Delimiter">,</span> <span class="Constant">"trace"</span><span class="Delimiter">)</span> << <span class="Constant">"run: incrementing callstack depth to "</span> << Trace_stream<span class="Delimiter">-></span>callstack_depth << end<span class="Delimiter">();</span>
assert<span class="Delimiter">(</span>Trace_stream<span class="Delimiter">-></span>callstack_depth < <span class="Constant">9000</span><span class="Delimiter">);</span> <span class="Comment">// 9998-101 plus cushion</span>
<span class="Delimiter">}</span>
Current_routine<span class="Delimiter">-></span>calls<span class="Delimiter">.</span>push_front<span class="Delimiter">(</span>call<span class="Delimiter">(</span>tmp_recipe<span class="Delimiter">.</span>at<span class="Delimiter">(</span><span class="Constant">0</span><span class="Delimiter">)));</span>
<span class="Identifier">continue</span><span class="Delimiter">;</span> <span class="Comment">// not done with caller; don't increment current_step_index()</span>
<span class="Delimiter">}</span>
<span class="Delimiter">:(before "End maybe_make_raw")</span>
<span class="Normal">if</span> <span class="Delimiter">(</span>starts_with<span class="Delimiter">(</span>caller<span class="Delimiter">.</span>name<span class="Delimiter">,</span> <span class="Constant">"run_"</span><span class="Delimiter">))</span>
r<span class="Delimiter">.</span>properties<span class="Delimiter">.</span>push_back<span class="Delimiter">(</span>pair<string<span class="Delimiter">,</span> string_tree*><span class="Delimiter">(</span><span class="Constant">"raw"</span><span class="Delimiter">,</span> <span class="Constant">NULL</span><span class="Delimiter">));</span>
<span class="Delimiter">:(scenario run_multiple)</span>
def main [
run [
<span class="Constant">1</span>:number<span class="Special"> <- </span>copy <span class="Constant">13</span>
]
run [
<span class="Constant">2</span>:number<span class="Special"> <- </span>copy <span class="Constant">13</span>
]
]
<span class="traceContains">+mem: storing 13 in location 1</span>
<span class="traceContains">+mem: storing 13 in location 2</span>
<span class="Comment">//: 'memory-should-contain' raises errors if specific locations aren't as expected</span>
<span class="Comment">//: Also includes some special support for checking strings.</span>
<span class="Delimiter">:(before "End Globals")</span>
<span class="Normal">bool</span> Scenario_testing_scenario = <span class="Constant">false</span><span class="Delimiter">;</span>
<span class="Delimiter">:(before "End Setup")</span>
Scenario_testing_scenario = <span class="Constant">false</span><span class="Delimiter">;</span>
<span class="Delimiter">:(scenario memory_check)</span>
<span class="Special">% Scenario_testing_scenario = true;</span>
<span class="Special">% Hide_errors = true;</span>
def main [
memory-should-contain [
<span class="Constant">1</span><span class="Special"> <- </span><span class="Constant">13</span>
]
]
<span class="traceContains">+run: checking location 1</span>
<span class="traceContains">+error: expected location '1' to contain 13 but saw 0</span>
<span class="Delimiter">:(before "End Primitive Recipe Declarations")</span>
MEMORY_SHOULD_CONTAIN<span class="Delimiter">,</span>
<span class="Delimiter">:(before "End Primitive Recipe Numbers")</span>
put<span class="Delimiter">(</span>Recipe_ordinal<span class="Delimiter">,</span> <span class="Constant">"memory-should-contain"</span><span class="Delimiter">,</span> MEMORY_SHOULD_CONTAIN<span class="Delimiter">);</span>
<span class="Delimiter">:(before "End Primitive Recipe Checks")</span>
<span class="Normal">case</span> MEMORY_SHOULD_CONTAIN: <span class="Delimiter">{</span>
<span class="Identifier">break</span><span class="Delimiter">;</span>
<span class="Delimiter">}</span>
<span class="Delimiter">:(before "End Primitive Recipe Implementations")</span>
<span class="Normal">case</span> MEMORY_SHOULD_CONTAIN: <span class="Delimiter">{</span>
<span class="Normal">if</span> <span class="Delimiter">(</span>!Passed<span class="Delimiter">)</span> <span class="Identifier">break</span><span class="Delimiter">;</span>
check_memory<span class="Delimiter">(</span>current_instruction<span class="Delimiter">().</span>ingredients<span class="Delimiter">.</span>at<span class="Delimiter">(</span><span class="Constant">0</span><span class="Delimiter">).</span>name<span class="Delimiter">);</span>
<span class="Identifier">break</span><span class="Delimiter">;</span>
<span class="Delimiter">}</span>
<span class="Delimiter">:(code)</span>
<span class="Normal">void</span> check_memory<span class="Delimiter">(</span><span class="Normal">const</span> string& s<span class="Delimiter">)</span> <span class="Delimiter">{</span>
istringstream in<span class="Delimiter">(</span>s<span class="Delimiter">);</span>
in >> std::noskipws<span class="Delimiter">;</span>
set<<span class="Normal">int</span>> locations_checked<span class="Delimiter">;</span>
<span class="Normal">while</span> <span class="Delimiter">(</span><span class="Constant">true</span><span class="Delimiter">)</span> <span class="Delimiter">{</span>
skip_whitespace_and_comments<span class="Delimiter">(</span>in<span class="Delimiter">);</span>
<span class="Normal">if</span> <span class="Delimiter">(</span>!has_data<span class="Delimiter">(</span>in<span class="Delimiter">))</span> <span class="Identifier">break</span><span class="Delimiter">;</span>
string lhs = next_word<span class="Delimiter">(</span>in<span class="Delimiter">);</span>
<span class="Normal">if</span> <span class="Delimiter">(</span>!is_integer<span class="Delimiter">(</span>lhs<span class="Delimiter">))</span> <span class="Delimiter">{</span>
check_type<span class="Delimiter">(</span>lhs<span class="Delimiter">,</span> in<span class="Delimiter">);</span>
<span class="Identifier">continue</span><span class="Delimiter">;</span>
<span class="Delimiter">}</span>
<span class="Normal">int</span> address = to_integer<span class="Delimiter">(</span>lhs<span class="Delimiter">);</span>
skip_whitespace_and_comments<span class="Delimiter">(</span>in<span class="Delimiter">);</span>
string _assign<span class="Delimiter">;</span> in >> _assign<span class="Delimiter">;</span> assert<span class="Delimiter">(</span>_assign == <span class="Constant">"<-"</span><span class="Delimiter">);</span>
skip_whitespace_and_comments<span class="Delimiter">(</span>in<span class="Delimiter">);</span>
string rhs = next_word<span class="Delimiter">(</span>in<span class="Delimiter">);</span>
<span class="Normal">if</span> <span class="Delimiter">(</span>!is_integer<span class="Delimiter">(</span>rhs<span class="Delimiter">)</span> && !is_noninteger<span class="Delimiter">(</span>rhs<span class="Delimiter">))</span> <span class="Delimiter">{</span>
<span class="Normal">if</span> <span class="Delimiter">(</span>Current_scenario && !Scenario_testing_scenario<span class="Delimiter">)</span>
<span class="Comment">// genuine test in a mu file</span>
raise << <span class="Constant">"</span><span class="cSpecial">\n</span><span class="Constant">F - "</span> << Current_scenario<span class="Delimiter">-></span>name << <span class="Constant">": location '"</span> << address << <span class="Constant">"' can't contain non-number "</span> << rhs << <span class="Constant">"</span><span class="cSpecial">\n</span><span class="Constant">"</span> << end<span class="Delimiter">();</span>
<span class="Normal">else</span>
<span class="Comment">// just testing scenario support</span>
raise << <span class="Constant">"location '"</span> << address << <span class="Constant">"' can't contain non-number "</span> << rhs << <span class="cSpecial">'\n'</span> << end<span class="Delimiter">();</span>
<span class="Normal">if</span> <span class="Delimiter">(</span>!Scenario_testing_scenario<span class="Delimiter">)</span> <span class="Delimiter">{</span>
Passed = <span class="Constant">false</span><span class="Delimiter">;</span>
++Num_failures<span class="Delimiter">;</span>
<span class="Delimiter">}</span>
<span class="Identifier">return</span><span class="Delimiter">;</span>
<span class="Delimiter">}</span>
<span class="Normal">double</span> value = to_double<span class="Delimiter">(</span>rhs<span class="Delimiter">);</span>
<span class="Normal">if</span> <span class="Delimiter">(</span>contains_key<span class="Delimiter">(</span>locations_checked<span class="Delimiter">,</span> address<span class="Delimiter">))</span>
raise << <span class="Constant">"duplicate expectation for location '"</span> << address << <span class="Constant">"'</span><span class="cSpecial">\n</span><span class="Constant">"</span> << end<span class="Delimiter">();</span>
trace<span class="Delimiter">(</span><span class="Constant">9999</span><span class="Delimiter">,</span> <span class="Constant">"run"</span><span class="Delimiter">)</span> << <span class="Constant">"checking location "</span> << address << end<span class="Delimiter">();</span>
<span class="Normal">if</span> <span class="Delimiter">(</span>get_or_insert<span class="Delimiter">(</span>Memory<span class="Delimiter">,</span> address<span class="Delimiter">)</span> != value<span class="Delimiter">)</span> <span class="Delimiter">{</span>
<span class="Normal">if</span> <span class="Delimiter">(</span>Current_scenario && !Scenario_testing_scenario<span class="Delimiter">)</span> <span class="Delimiter">{</span>
<span class="Comment">// genuine test in a mu file</span>
raise << <span class="Constant">"</span><span class="cSpecial">\n</span><span class="Constant">F - "</span> << Current_scenario<span class="Delimiter">-></span>name << <span class="Constant">": expected location '"</span> << address << <span class="Constant">"' to contain "</span> << no_scientific<span class="Delimiter">(</span>value<span class="Delimiter">)</span> << <span class="Constant">" but saw "</span> << no_scientific<span class="Delimiter">(</span>get_or_insert<span class="Delimiter">(</span>Memory<span class="Delimiter">,</span> address<span class="Delimiter">))</span> << <span class="cSpecial">'\n'</span> << end<span class="Delimiter">();</span>
<span class="Delimiter">}</span>
<span class="Normal">else</span> <span class="Delimiter">{</span>
<span class="Comment">// just testing scenario support</span>
raise << <span class="Constant">"expected location '"</span> << address << <span class="Constant">"' to contain "</span> << no_scientific<span class="Delimiter">(</span>value<span class="Delimiter">)</span> << <span class="Constant">" but saw "</span> << no_scientific<span class="Delimiter">(</span>get_or_insert<span class="Delimiter">(</span>Memory<span class="Delimiter">,</span> address<span class="Delimiter">))</span> << <span class="cSpecial">'\n'</span> << end<span class="Delimiter">();</span>
<span class="Delimiter">}</span>
<span class="Normal">if</span> <span class="Delimiter">(</span>!Scenario_testing_scenario<span class="Delimiter">)</span> <span class="Delimiter">{</span>
Passed = <span class="Constant">false</span><span class="Delimiter">;</span>
++Num_failures<span class="Delimiter">;</span>
<span class="Delimiter">}</span>
<span class="Identifier">return</span><span class="Delimiter">;</span>
<span class="Delimiter">}</span>
locations_checked<span class="Delimiter">.</span>insert<span class="Delimiter">(</span>address<span class="Delimiter">);</span>
<span class="Delimiter">}</span>
<span class="Delimiter">}</span>
<span class="Normal">void</span> check_type<span class="Delimiter">(</span><span class="Normal">const</span> string& lhs<span class="Delimiter">,</span> istream& in<span class="Delimiter">)</span> <span class="Delimiter">{</span>
reagent x<span class="Delimiter">(</span>lhs<span class="Delimiter">);</span>
<span class="Normal">if</span> <span class="Delimiter">(</span>x<span class="Delimiter">.</span>type<span class="Delimiter">-></span>name == <span class="Constant">"array"</span>
&& x<span class="Delimiter">.</span>type<span class="Delimiter">-></span>right && x<span class="Delimiter">.</span>type<span class="Delimiter">-></span>right<span class="Delimiter">-></span>name == <span class="Constant">"character"</span>
&& !x<span class="Delimiter">.</span>type<span class="Delimiter">-></span>right<span class="Delimiter">-></span>right<span class="Delimiter">)</span> <span class="Delimiter">{</span>
x<span class="Delimiter">.</span>set_value<span class="Delimiter">(</span>to_integer<span class="Delimiter">(</span>x<span class="Delimiter">.</span>name<span class="Delimiter">));</span>
skip_whitespace_and_comments<span class="Delimiter">(</span>in<span class="Delimiter">);</span>
string _assign = next_word<span class="Delimiter">(</span>in<span class="Delimiter">);</span>
assert<span class="Delimiter">(</span>_assign == <span class="Constant">"<-"</span><span class="Delimiter">);</span>
skip_whitespace_and_comments<span class="Delimiter">(</span>in<span class="Delimiter">);</span>
string literal = next_word<span class="Delimiter">(</span>in<span class="Delimiter">);</span>
<span class="Normal">int</span> address = x<span class="Delimiter">.</span>value<span class="Delimiter">;</span>
<span class="Comment">// exclude quoting brackets</span>
assert<span class="Delimiter">(</span>*literal<span class="Delimiter">.</span>begin<span class="Delimiter">()</span> == <span class="Constant">'['</span><span class="Delimiter">);</span> literal<span class="Delimiter">.</span>erase<span class="Delimiter">(</span>literal<span class="Delimiter">.</span>begin<span class="Delimiter">());</span>
assert<span class="Delimiter">(</span>*--literal<span class="Delimiter">.</span>end<span class="Delimiter">()</span> == <span class="Constant">']'</span><span class="Delimiter">);</span> literal<span class="Delimiter">.</span>erase<span class="Delimiter">(</span>--literal<span class="Delimiter">.</span>end<span class="Delimiter">());</span>
check_string<span class="Delimiter">(</span>address<span class="Delimiter">,</span> literal<span class="Delimiter">);</span>
<span class="Identifier">return</span><span class="Delimiter">;</span>
<span class="Delimiter">}</span>
<span class="Comment">// End Scenario Type Cases</span>
raise << <span class="Constant">"don't know how to check memory for '"</span> << lhs << <span class="Constant">"'</span><span class="cSpecial">\n</span><span class="Constant">"</span> << end<span class="Delimiter">();</span>
<span class="Delimiter">}</span>
<span class="Normal">void</span> check_string<span class="Delimiter">(</span><span class="Normal">int</span> address<span class="Delimiter">,</span> <span class="Normal">const</span> string& literal<span class="Delimiter">)</span> <span class="Delimiter">{</span>
trace<span class="Delimiter">(</span><span class="Constant">9999</span><span class="Delimiter">,</span> <span class="Constant">"run"</span><span class="Delimiter">)</span> << <span class="Constant">"checking string length at "</span> << address << end<span class="Delimiter">();</span>
<span class="Normal">if</span> <span class="Delimiter">(</span>get_or_insert<span class="Delimiter">(</span>Memory<span class="Delimiter">,</span> address<span class="Delimiter">)</span> != SIZE<span class="Delimiter">(</span>literal<span class="Delimiter">))</span> <span class="Delimiter">{</span>
<span class="Normal">if</span> <span class="Delimiter">(</span>Current_scenario && !Scenario_testing_scenario<span class="Delimiter">)</span>
raise << <span class="Constant">"</span><span class="cSpecial">\n</span><span class="Constant">F - "</span> << Current_scenario<span class="Delimiter">-></span>name << <span class="Constant">": expected location '"</span> << address << <span class="Constant">"' to contain length "</span> << SIZE<span class="Delimiter">(</span>literal<span class="Delimiter">)</span> << <span class="Constant">" of string ["</span> << literal << <span class="Constant">"] but saw "</span> << no_scientific<span class="Delimiter">(</span>get_or_insert<span class="Delimiter">(</span>Memory<span class="Delimiter">,</span> address<span class="Delimiter">))</span> << <span class="Constant">" ("</span> << read_mu_string<span class="Delimiter">(</span>address<span class="Delimiter">)</span> << <span class="Constant">")</span><span class="cSpecial">\n</span><span class="Constant">"</span> << end<span class="Delimiter">();</span>
<span class="Normal">else</span>
raise << <span class="Constant">"expected location '"</span> << address << <span class="Constant">"' to contain length "</span> << SIZE<span class="Delimiter">(</span>literal<span class="Delimiter">)</span> << <span class="Constant">" of string ["</span> << literal << <span class="Constant">"] but saw "</span> << no_scientific<span class="Delimiter">(</span>get_or_insert<span class="Delimiter">(</span>Memory<span class="Delimiter">,</span> address<span class="Delimiter">))</span> << <span class="cSpecial">'\n'</span> << end<span class="Delimiter">();</span>
<span class="Normal">if</span> <span class="Delimiter">(</span>!Scenario_testing_scenario<span class="Delimiter">)</span> <span class="Delimiter">{</span>
Passed = <span class="Constant">false</span><span class="Delimiter">;</span>
++Num_failures<span class="Delimiter">;</span>
<span class="Delimiter">}</span>
<span class="Identifier">return</span><span class="Delimiter">;</span>
<span class="Delimiter">}</span>
++address<span class="Delimiter">;</span> <span class="Comment">// now skip length</span>
<span class="Normal">for</span> <span class="Delimiter">(</span><span class="Normal">int</span> i = <span class="Constant">0</span><span class="Delimiter">;</span> i < SIZE<span class="Delimiter">(</span>literal<span class="Delimiter">);</span> ++i<span class="Delimiter">)</span> <span class="Delimiter">{</span>
trace<span class="Delimiter">(</span><span class="Constant">9999</span><span class="Delimiter">,</span> <span class="Constant">"run"</span><span class="Delimiter">)</span> << <span class="Constant">"checking location "</span> << address+i << end<span class="Delimiter">();</span>
<span class="Normal">if</span> <span class="Delimiter">(</span>get_or_insert<span class="Delimiter">(</span>Memory<span class="Delimiter">,</span> address+i<span class="Delimiter">)</span> != literal<span class="Delimiter">.</span>at<span class="Delimiter">(</span>i<span class="Delimiter">))</span> <span class="Delimiter">{</span>
<span class="Normal">if</span> <span class="Delimiter">(</span>Current_scenario && !Scenario_testing_scenario<span class="Delimiter">)</span> <span class="Delimiter">{</span>
<span class="Comment">// genuine test in a mu file</span>
raise << <span class="Constant">"</span><span class="cSpecial">\n</span><span class="Constant">F - "</span> << Current_scenario<span class="Delimiter">-></span>name << <span class="Constant">": expected location "</span> << <span class="Delimiter">(</span>address+i<span class="Delimiter">)</span> << <span class="Constant">" to contain "</span> << literal<span class="Delimiter">.</span>at<span class="Delimiter">(</span>i<span class="Delimiter">)</span> << <span class="Constant">" but saw "</span> << no_scientific<span class="Delimiter">(</span>get_or_insert<span class="Delimiter">(</span>Memory<span class="Delimiter">,</span> address+i<span class="Delimiter">))</span> << <span class="Constant">" ('"</span> << <span class="Normal">static_cast</span><<span class="Normal">char</span>><span class="Delimiter">(</span>get_or_insert<span class="Delimiter">(</span>Memory<span class="Delimiter">,</span> address+i<span class="Delimiter">))</span> << <span class="Constant">"')</span><span class="cSpecial">\n</span><span class="Constant">"</span> << end<span class="Delimiter">();</span>
<span class="Delimiter">}</span>
<span class="Normal">else</span> <span class="Delimiter">{</span>
<span class="Comment">// just testing scenario support</span>
raise << <span class="Constant">"expected location "</span> << <span class="Delimiter">(</span>address+i<span class="Delimiter">)</span> << <span class="Constant">" to contain "</span> << literal<span class="Delimiter">.</span>at<span class="Delimiter">(</span>i<span class="Delimiter">)</span> << <span class="Constant">" but saw "</span> << no_scientific<span class="Delimiter">(</span>get_or_insert<span class="Delimiter">(</span>Memory<span class="Delimiter">,</span> address+i<span class="Delimiter">))</span> << <span class="cSpecial">'\n'</span> << end<span class="Delimiter">();</span>
<span class="Delimiter">}</span>
<span class="Normal">if</span> <span class="Delimiter">(</span>!Scenario_testing_scenario<span class="Delimiter">)</span> <span class="Delimiter">{</span>
Passed = <span class="Constant">false</span><span class="Delimiter">;</span>
++Num_failures<span class="Delimiter">;</span>
<span class="Delimiter">}</span>
<span class="Identifier">return</span><span class="Delimiter">;</span>
<span class="Delimiter">}</span>
<span class="Delimiter">}</span>
<span class="Delimiter">}</span>
<span class="Delimiter">:(scenario memory_check_multiple)</span>
<span class="Special">% Scenario_testing_scenario = true;</span>
<span class="Special">% Hide_errors = true;</span>
def main [
memory-should-contain [
<span class="Constant">1</span><span class="Special"> <- </span><span class="Constant">0</span>
<span class="Constant">1</span><span class="Special"> <- </span><span class="Constant">0</span>
]
]
<span class="traceContains">+error: duplicate expectation for location '1'</span>
<span class="Delimiter">:(scenario memory_check_string_length)</span>
<span class="Special">% Scenario_testing_scenario = true;</span>
<span class="Special">% Hide_errors = true;</span>
def main [
<span class="Constant">1</span>:number<span class="Special"> <- </span>copy <span class="Constant">3</span>
<span class="Constant">2</span>:number<span class="Special"> <- </span>copy <span class="Constant">97</span> <span class="Comment"># 'a'</span>
<span class="Constant">3</span>:number<span class="Special"> <- </span>copy <span class="Constant">98</span> <span class="Comment"># 'b'</span>
<span class="Constant">4</span>:number<span class="Special"> <- </span>copy <span class="Constant">99</span> <span class="Comment"># 'c'</span>
memory-should-contain [
<span class="Constant">1</span>:array:character<span class="Special"> <- </span>[ab]
]
]
<span class="traceContains">+error: expected location '1' to contain length 2 of string [ab] but saw 3</span>
<span class="Delimiter">:(scenario memory_check_string)</span>
def main [
<span class="Constant">1</span>:number<span class="Special"> <- </span>copy <span class="Constant">3</span>
<span class="Constant">2</span>:number<span class="Special"> <- </span>copy <span class="Constant">97</span> <span class="Comment"># 'a'</span>
<span class="Constant">3</span>:number<span class="Special"> <- </span>copy <span class="Constant">98</span> <span class="Comment"># 'b'</span>
<span class="Constant">4</span>:number<span class="Special"> <- </span>copy <span class="Constant">99</span> <span class="Comment"># 'c'</span>
memory-should-contain [
<span class="Constant">1</span>:array:character<span class="Special"> <- </span>[abc]
]
]
<span class="traceContains">+run: checking string length at 1</span>
<span class="traceContains">+run: checking location 2</span>
<span class="traceContains">+run: checking location 3</span>
<span class="traceContains">+run: checking location 4</span>
<span class="Delimiter">:(scenario memory_invalid_string_check)</span>
<span class="Special">% Scenario_testing_scenario = true;</span>
<span class="Special">% Hide_errors = true;</span>
def main [
memory-should-contain [
<span class="Constant">1</span><span class="Special"> <- </span>[abc]
]
]
<span class="traceContains">+error: location '1' can't contain non-number [abc]</span>
<span class="Delimiter">:(scenario memory_check_with_comment)</span>
<span class="Special">% Scenario_testing_scenario = true;</span>
<span class="Special">% Hide_errors = true;</span>
def main [
memory-should-contain [
<span class="Constant">1</span><span class="Special"> <- </span><span class="Constant">34</span> <span class="Comment"># comment</span>
]
]
<span class="traceAbsent">-error: location 1 can't contain non-number 34 # comment</span>
<span class="Comment"># but there'll be an error signalled by memory-should-contain</span>
<span class="Delimiter">:(code)</span>
<span class="Comment">//: 'trace-should-contain' is like the '+' lines in our scenarios so far</span>
<span class="Comment">// Like runs of contiguous '+' lines, order is important. The trace checks</span>
<span class="Comment">// that the lines are present *and* in the specified sequence. (There can be</span>
<span class="Comment">// other lines in between.)</span>
<span class="Delimiter">:(scenario trace_check_fails)</span>
<span class="Special">% Scenario_testing_scenario = true;</span>
<span class="Special">% Hide_errors = true;</span>
def main [
trace-should-contain [
<span class="Normal">a</span>: b
<span class="Normal">a</span>: d
]
]
<span class="traceContains">+error: missing [b] in trace with label a</span>
<span class="Delimiter">:(before "End Primitive Recipe Declarations")</span>
TRACE_SHOULD_CONTAIN<span class="Delimiter">,</span>
<span class="Delimiter">:(before "End Primitive Recipe Numbers")</span>
put<span class="Delimiter">(</span>Recipe_ordinal<span class="Delimiter">,</span> <span class="Constant">"trace-should-contain"</span><span class="Delimiter">,</span> TRACE_SHOULD_CONTAIN<span class="Delimiter">);</span>
<span class="Delimiter">:(before "End Primitive Recipe Checks")</span>
<span class="Normal">case</span> TRACE_SHOULD_CONTAIN: <span class="Delimiter">{</span>
<span class="Identifier">break</span><span class="Delimiter">;</span>
<span class="Delimiter">}</span>
<span class="Delimiter">:(before "End Primitive Recipe Implementations")</span>
<span class="Normal">case</span> TRACE_SHOULD_CONTAIN: <span class="Delimiter">{</span>
<span class="Normal">if</span> <span class="Delimiter">(</span>!Passed<span class="Delimiter">)</span> <span class="Identifier">break</span><span class="Delimiter">;</span>
check_trace<span class="Delimiter">(</span>current_instruction<span class="Delimiter">().</span>ingredients<span class="Delimiter">.</span>at<span class="Delimiter">(</span><span class="Constant">0</span><span class="Delimiter">).</span>name<span class="Delimiter">);</span>
<span class="Identifier">break</span><span class="Delimiter">;</span>
<span class="Delimiter">}</span>
<span class="Delimiter">:(code)</span>
<span class="Comment">// simplified version of check_trace_contents() that emits errors rather</span>
<span class="Comment">// than just printing to stderr</span>
<span class="Normal">void</span> check_trace<span class="Delimiter">(</span><span class="Normal">const</span> string& expected<span class="Delimiter">)</span> <span class="Delimiter">{</span>
Trace_stream<span class="Delimiter">-></span>newline<span class="Delimiter">();</span>
vector<trace_line> expected_lines = parse_trace<span class="Delimiter">(</span>expected<span class="Delimiter">);</span>
<span class="Normal">if</span> <span class="Delimiter">(</span>expected_lines<span class="Delimiter">.</span>empty<span class="Delimiter">())</span> <span class="Identifier">return</span><span class="Delimiter">;</span>
<span class="Normal">int</span> curr_expected_line = <span class="Constant">0</span><span class="Delimiter">;</span>
<span class="Normal">for</span> <span class="Delimiter">(</span>vector<trace_line>::iterator p = Trace_stream<span class="Delimiter">-></span>past_lines<span class="Delimiter">.</span>begin<span class="Delimiter">();</span> p != Trace_stream<span class="Delimiter">-></span>past_lines<span class="Delimiter">.</span>end<span class="Delimiter">();</span> ++p<span class="Delimiter">)</span> <span class="Delimiter">{</span>
<span class="Normal">if</span> <span class="Delimiter">(</span>expected_lines<span class="Delimiter">.</span>at<span class="Delimiter">(</span>curr_expected_line<span class="Delimiter">).</span>label != p<span class="Delimiter">-></span>label<span class="Delimiter">)</span> <span class="Identifier">continue</span><span class="Delimiter">;</span>
<span class="Normal">if</span> <span class="Delimiter">(</span>expected_lines<span class="Delimiter">.</span>at<span class="Delimiter">(</span>curr_expected_line<span class="Delimiter">).</span>contents != trim<span class="Delimiter">(</span>p<span class="Delimiter">-></span>contents<span class="Delimiter">))</span> <span class="Identifier">continue</span><span class="Delimiter">;</span>
<span class="Comment">// match</span>
++curr_expected_line<span class="Delimiter">;</span>
<span class="Normal">if</span> <span class="Delimiter">(</span>curr_expected_line == SIZE<span class="Delimiter">(</span>expected_lines<span class="Delimiter">))</span> <span class="Identifier">return</span><span class="Delimiter">;</span>
<span class="Delimiter">}</span>
raise << <span class="Constant">"missing ["</span> << expected_lines<span class="Delimiter">.</span>at<span class="Delimiter">(</span>curr_expected_line<span class="Delimiter">).</span>contents << <span class="Constant">"] "</span>
<< <span class="Constant">"in trace with label "</span> << expected_lines<span class="Delimiter">.</span>at<span class="Delimiter">(</span>curr_expected_line<span class="Delimiter">).</span>label << <span class="cSpecial">'\n'</span> << end<span class="Delimiter">();</span>
Passed = <span class="Constant">false</span><span class="Delimiter">;</span>
<span class="Delimiter">}</span>
vector<trace_line> parse_trace<span class="Delimiter">(</span><span class="Normal">const</span> string& expected<span class="Delimiter">)</span> <span class="Delimiter">{</span>
vector<string> buf = split<span class="Delimiter">(</span>expected<span class="Delimiter">,</span> <span class="Constant">"</span><span class="cSpecial">\n</span><span class="Constant">"</span><span class="Delimiter">);</span>
vector<trace_line> result<span class="Delimiter">;</span>
<span class="Normal">for</span> <span class="Delimiter">(</span><span class="Normal">int</span> i = <span class="Constant">0</span><span class="Delimiter">;</span> i < SIZE<span class="Delimiter">(</span>buf<span class="Delimiter">);</span> ++i<span class="Delimiter">)</span> <span class="Delimiter">{</span>
buf<span class="Delimiter">.</span>at<span class="Delimiter">(</span>i<span class="Delimiter">)</span> = trim<span class="Delimiter">(</span>buf<span class="Delimiter">.</span>at<span class="Delimiter">(</span>i<span class="Delimiter">));</span>
<span class="Normal">if</span> <span class="Delimiter">(</span>buf<span class="Delimiter">.</span>at<span class="Delimiter">(</span>i<span class="Delimiter">).</span>empty<span class="Delimiter">())</span> <span class="Identifier">continue</span><span class="Delimiter">;</span>
<span class="Normal">int</span> delim = buf<span class="Delimiter">.</span>at<span class="Delimiter">(</span>i<span class="Delimiter">).</span>find<span class="Delimiter">(</span><span class="Constant">": "</span><span class="Delimiter">);</span>
result<span class="Delimiter">.</span>push_back<span class="Delimiter">(</span>trace_line<span class="Delimiter">(</span>trim<span class="Delimiter">(</span>buf<span class="Delimiter">.</span>at<span class="Delimiter">(</span>i<span class="Delimiter">).</span>substr<span class="Delimiter">(</span><span class="Constant">0</span><span class="Delimiter">,</span> delim<span class="Delimiter">)),</span> trim<span class="Delimiter">(</span>buf<span class="Delimiter">.</span>at<span class="Delimiter">(</span>i<span class="Delimiter">).</span>substr<span class="Delimiter">(</span>delim+<span class="Constant">2</span><span class="Delimiter">))));</span>
<span class="Delimiter">}</span>
<span class="Identifier">return</span> result<span class="Delimiter">;</span>
<span class="Delimiter">}</span>
<span class="Delimiter">:(scenario trace_check_fails_in_nonfirst_line)</span>
<span class="Special">% Scenario_testing_scenario = true;</span>
<span class="Special">% Hide_errors = true;</span>
def main [
run [
trace <span class="Constant">1</span><span class="Delimiter">,</span> [a]<span class="Delimiter">,</span> [b]
]
trace-should-contain [
<span class="Normal">a</span>: b
<span class="Normal">a</span>: d
]
]
<span class="traceContains">+error: missing [d] in trace with label a</span>
<span class="Delimiter">:(scenario trace_check_passes_silently)</span>
<span class="Special">% Scenario_testing_scenario = true;</span>
def main [
run [
trace <span class="Constant">1</span><span class="Delimiter">,</span> [a]<span class="Delimiter">,</span> [b]
]
trace-should-contain [
<span class="Normal">a</span>: b
]
]
<span class="traceAbsent">-error: missing [b] in trace with label a</span>
$error: <span class="Constant">0</span>
<span class="Comment">//: 'trace-should-not-contain' is like the '-' lines in our scenarios so far</span>
<span class="Comment">//: Each trace line is separately checked for absense. Order is *not*</span>
<span class="Comment">//: important, so you can't say things like "B should not exist after A."</span>
<span class="Delimiter">:(scenario trace_negative_check_fails)</span>
<span class="Special">% Scenario_testing_scenario = true;</span>
<span class="Special">% Hide_errors = true;</span>
def main [
run [
trace <span class="Constant">1</span><span class="Delimiter">,</span> [a]<span class="Delimiter">,</span> [b]
]
trace-should-<span class="Normal">not</span>-contain [
<span class="Normal">a</span>: b
]
]
<span class="traceContains">+error: unexpected [b] in trace with label a</span>
<span class="Delimiter">:(before "End Primitive Recipe Declarations")</span>
TRACE_SHOULD_NOT_CONTAIN<span class="Delimiter">,</span>
<span class="Delimiter">:(before "End Primitive Recipe Numbers")</span>
put<span class="Delimiter">(</span>Recipe_ordinal<span class="Delimiter">,</span> <span class="Constant">"trace-should-not-contain"</span><span class="Delimiter">,</span> TRACE_SHOULD_NOT_CONTAIN<span class="Delimiter">);</span>
<span class="Delimiter">:(before "End Primitive Recipe Checks")</span>
<span class="Normal">case</span> TRACE_SHOULD_NOT_CONTAIN: <span class="Delimiter">{</span>
<span class="Identifier">break</span><span class="Delimiter">;</span>
<span class="Delimiter">}</span>
<span class="Delimiter">:(before "End Primitive Recipe Implementations")</span>
<span class="Normal">case</span> TRACE_SHOULD_NOT_CONTAIN: <span class="Delimiter">{</span>
<span class="Normal">if</span> <span class="Delimiter">(</span>!Passed<span class="Delimiter">)</span> <span class="Identifier">break</span><span class="Delimiter">;</span>
check_trace_missing<span class="Delimiter">(</span>current_instruction<span class="Delimiter">().</span>ingredients<span class="Delimiter">.</span>at<span class="Delimiter">(</span><span class="Constant">0</span><span class="Delimiter">).</span>name<span class="Delimiter">);</span>
<span class="Identifier">break</span><span class="Delimiter">;</span>
<span class="Delimiter">}</span>
<span class="Delimiter">:(code)</span>
<span class="Comment">// simplified version of check_trace_contents() that emits errors rather</span>
<span class="Comment">// than just printing to stderr</span>
<span class="Normal">bool</span> check_trace_missing<span class="Delimiter">(</span><span class="Normal">const</span> string& in<span class="Delimiter">)</span> <span class="Delimiter">{</span>
Trace_stream<span class="Delimiter">-></span>newline<span class="Delimiter">();</span>
vector<trace_line> lines = parse_trace<span class="Delimiter">(</span>in<span class="Delimiter">);</span>
<span class="Normal">for</span> <span class="Delimiter">(</span><span class="Normal">int</span> i = <span class="Constant">0</span><span class="Delimiter">;</span> i < SIZE<span class="Delimiter">(</span>lines<span class="Delimiter">);</span> ++i<span class="Delimiter">)</span> <span class="Delimiter">{</span>
<span class="Normal">if</span> <span class="Delimiter">(</span>trace_count<span class="Delimiter">(</span>lines<span class="Delimiter">.</span>at<span class="Delimiter">(</span>i<span class="Delimiter">).</span>label<span class="Delimiter">,</span> lines<span class="Delimiter">.</span>at<span class="Delimiter">(</span>i<span class="Delimiter">).</span>contents<span class="Delimiter">)</span> != <span class="Constant">0</span><span class="Delimiter">)</span> <span class="Delimiter">{</span>
raise << <span class="Constant">"unexpected ["</span> << lines<span class="Delimiter">.</span>at<span class="Delimiter">(</span>i<span class="Delimiter">).</span>contents << <span class="Constant">"] in trace with label "</span> << lines<span class="Delimiter">.</span>at<span class="Delimiter">(</span>i<span class="Delimiter">).</span>label << <span class="cSpecial">'\n'</span> << end<span class="Delimiter">();</span>
Passed = <span class="Constant">false</span><span class="Delimiter">;</span>
<span class="Identifier">return</span> <span class="Constant">false</span><span class="Delimiter">;</span>
<span class="Delimiter">}</span>
<span class="Delimiter">}</span>
<span class="Identifier">return</span> <span class="Constant">true</span><span class="Delimiter">;</span>
<span class="Delimiter">}</span>
<span class="Delimiter">:(scenario trace_negative_check_passes_silently)</span>
<span class="Special">% Scenario_testing_scenario = true;</span>
def main [
trace-should-<span class="Normal">not</span>-contain [
<span class="Normal">a</span>: b
]
]
<span class="traceAbsent">-error: unexpected [b] in trace with label a</span>
$error: <span class="Constant">0</span>
<span class="Delimiter">:(scenario trace_negative_check_fails_on_any_unexpected_line)</span>
<span class="Special">% Scenario_testing_scenario = true;</span>
<span class="Special">% Hide_errors = true;</span>
def main [
run [
trace <span class="Constant">1</span><span class="Delimiter">,</span> [a]<span class="Delimiter">,</span> [d]
]
trace-should-<span class="Normal">not</span>-contain [
<span class="Normal">a</span>: b
<span class="Normal">a</span>: d
]
]
<span class="traceContains">+error: unexpected [d] in trace with label a</span>
<span class="Delimiter">:(scenario trace_count_check)</span>
def main [
run [
trace <span class="Constant">1</span><span class="Delimiter">,</span> [a]<span class="Delimiter">,</span> [foo]
]
check-trace-count-<span class="Normal">for</span>-label <span class="Constant">1</span><span class="Delimiter">,</span> [a]
]
<span class="Delimiter">:(before "End Primitive Recipe Declarations")</span>
CHECK_TRACE_COUNT_FOR_LABEL<span class="Delimiter">,</span>
<span class="Delimiter">:(before "End Primitive Recipe Numbers")</span>
put<span class="Delimiter">(</span>Recipe_ordinal<span class="Delimiter">,</span> <span class="Constant">"check-trace-count-for-label"</span><span class="Delimiter">,</span> CHECK_TRACE_COUNT_FOR_LABEL<span class="Delimiter">);</span>
<span class="Delimiter">:(before "End Primitive Recipe Checks")</span>
<span class="Normal">case</span> CHECK_TRACE_COUNT_FOR_LABEL: <span class="Delimiter">{</span>
<span class="Normal">if</span> <span class="Delimiter">(</span>SIZE<span class="Delimiter">(</span>inst<span class="Delimiter">.</span>ingredients<span class="Delimiter">)</span> != <span class="Constant">2</span><span class="Delimiter">)</span> <span class="Delimiter">{</span>
raise << maybe<span class="Delimiter">(</span>get<span class="Delimiter">(</span>Recipe<span class="Delimiter">,</span> r<span class="Delimiter">).</span>name<span class="Delimiter">)</span> << <span class="Constant">"'check-trace-count-for-label' requires exactly two ingredients, but got '"</span> << to_original_string<span class="Delimiter">(</span>inst<span class="Delimiter">)</span> << <span class="Constant">"'</span><span class="cSpecial">\n</span><span class="Constant">"</span> << end<span class="Delimiter">();</span>
<span class="Identifier">break</span><span class="Delimiter">;</span>
<span class="Delimiter">}</span>
<span class="Normal">if</span> <span class="Delimiter">(</span>!is_mu_number<span class="Delimiter">(</span>inst<span class="Delimiter">.</span>ingredients<span class="Delimiter">.</span>at<span class="Delimiter">(</span><span class="Constant">0</span><span class="Delimiter">)))</span> <span class="Delimiter">{</span>
raise << maybe<span class="Delimiter">(</span>get<span class="Delimiter">(</span>Recipe<span class="Delimiter">,</span> r<span class="Delimiter">).</span>name<span class="Delimiter">)</span> << <span class="Constant">"first ingredient of 'check-trace-count-for-label' should be a number (count), but got '"</span> << inst<span class="Delimiter">.</span>ingredients<span class="Delimiter">.</span>at<span class="Delimiter">(</span><span class="Constant">0</span><span class="Delimiter">).</span>original_string << <span class="Constant">"'</span><span class="cSpecial">\n</span><span class="Constant">"</span> << end<span class="Delimiter">();</span>
<span class="Identifier">break</span><span class="Delimiter">;</span>
<span class="Delimiter">}</span>
<span class="Normal">if</span> <span class="Delimiter">(</span>!is_literal_string<span class="Delimiter">(</span>inst<span class="Delimiter">.</span>ingredients<span class="Delimiter">.</span>at<span class="Delimiter">(</span><span class="Constant">1</span><span class="Delimiter">)))</span> <span class="Delimiter">{</span>
raise << maybe<span class="Delimiter">(</span>get<span class="Delimiter">(</span>Recipe<span class="Delimiter">,</span> r<span class="Delimiter">).</span>name<span class="Delimiter">)</span> << <span class="Constant">"second ingredient of 'check-trace-count-for-label' should be a literal string (label), but got '"</span> << inst<span class="Delimiter">.</span>ingredients<span class="Delimiter">.</span>at<span class="Delimiter">(</span><span class="Constant">1</span><span class="Delimiter">).</span>original_string << <span class="Constant">"'</span><span class="cSpecial">\n</span><span class="Constant">"</span> << end<span class="Delimiter">();</span>
<span class="Identifier">break</span><span class="Delimiter">;</span>
<span class="Delimiter">}</span>
<span class="Identifier">break</span><span class="Delimiter">;</span>
<span class="Delimiter">}</span>
<span class="Delimiter">:(before "End Primitive Recipe Implementations")</span>
<span class="Normal">case</span> CHECK_TRACE_COUNT_FOR_LABEL: <span class="Delimiter">{</span>
<span class="Normal">if</span> <span class="Delimiter">(</span>!Passed<span class="Delimiter">)</span> <span class="Identifier">break</span><span class="Delimiter">;</span>
<span class="Normal">int</span> expected_count = ingredients<span class="Delimiter">.</span>at<span class="Delimiter">(</span><span class="Constant">0</span><span class="Delimiter">).</span>at<span class="Delimiter">(</span><span class="Constant">0</span><span class="Delimiter">);</span>
string label = current_instruction<span class="Delimiter">().</span>ingredients<span class="Delimiter">.</span>at<span class="Delimiter">(</span><span class="Constant">1</span><span class="Delimiter">).</span>name<span class="Delimiter">;</span>
<span class="Normal">int</span> count = trace_count<span class="Delimiter">(</span>label<span class="Delimiter">);</span>
<span class="Normal">if</span> <span class="Delimiter">(</span>count != expected_count<span class="Delimiter">)</span> <span class="Delimiter">{</span>
<span class="Normal">if</span> <span class="Delimiter">(</span>Current_scenario && !Scenario_testing_scenario<span class="Delimiter">)</span> <span class="Delimiter">{</span>
<span class="Comment">// genuine test in a mu file</span>
raise << <span class="Constant">"</span><span class="cSpecial">\n</span><span class="Constant">F - "</span> << Current_scenario<span class="Delimiter">-></span>name << <span class="Constant">": "</span> << maybe<span class="Delimiter">(</span>current_recipe_name<span class="Delimiter">())</span> << <span class="Constant">"expected "</span> << expected_count << <span class="Constant">" lines in trace with label "</span> << label << <span class="Constant">" in trace: "</span> << end<span class="Delimiter">();</span>
DUMP<span class="Delimiter">(</span>label<span class="Delimiter">);</span>
<span class="Delimiter">}</span>
<span class="Normal">else</span> <span class="Delimiter">{</span>
<span class="Comment">// just testing scenario support</span>
raise << maybe<span class="Delimiter">(</span>current_recipe_name<span class="Delimiter">())</span> << <span class="Constant">"expected "</span> << expected_count << <span class="Constant">" lines in trace with label "</span> << label << <span class="Constant">" in trace</span><span class="cSpecial">\n</span><span class="Constant">"</span> << end<span class="Delimiter">();</span>
<span class="Delimiter">}</span>
<span class="Normal">if</span> <span class="Delimiter">(</span>!Scenario_testing_scenario<span class="Delimiter">)</span> <span class="Delimiter">{</span>
Passed = <span class="Constant">false</span><span class="Delimiter">;</span>
++Num_failures<span class="Delimiter">;</span>
<span class="Delimiter">}</span>
<span class="Delimiter">}</span>
<span class="Identifier">break</span><span class="Delimiter">;</span>
<span class="Delimiter">}</span>
<span class="Delimiter">:(scenario trace_count_check_2)</span>
<span class="Special">% Scenario_testing_scenario = true;</span>
<span class="Special">% Hide_errors = true;</span>
def main [
run [
trace <span class="Constant">1</span><span class="Delimiter">,</span> [a]<span class="Delimiter">,</span> [foo]
]
check-trace-count-<span class="Normal">for</span>-label <span class="Constant">2</span><span class="Delimiter">,</span> [a]
]
<span class="traceContains">+error: main: expected 2 lines in trace with label a in trace</span>
<span class="Comment">//: Minor detail: ignore 'system' calls in scenarios, since anything we do</span>
<span class="Comment">//: with them is by definition impossible to test through mu.</span>
<span class="Delimiter">:(after "case _SYSTEM:")</span>
<span class="Normal">if</span> <span class="Delimiter">(</span>Current_scenario<span class="Delimiter">)</span> <span class="Identifier">break</span><span class="Delimiter">;</span>
<span class="SalientComment">//:: Warn if people use '_' manually in function names. They're reserved for internal use.</span>
<span class="Delimiter">:(scenario recipe_name_with_underscore)</span>
<span class="Special">% Hide_errors = true;</span>
def foo_bar [
]
<span class="traceContains">+error: foo_bar: don't create recipes with '_' in the name</span>
<span class="Delimiter">:(before "End recipe Fields")</span>
<span class="Normal">bool</span> is_autogenerated<span class="Delimiter">;</span>
<span class="Delimiter">:(before "End recipe Constructor")</span>
is_autogenerated = <span class="Constant">false</span><span class="Delimiter">;</span>
<span class="Delimiter">:(code)</span>
<span class="Normal">void</span> mark_autogenerated<span class="Delimiter">(</span>recipe_ordinal r<span class="Delimiter">)</span> <span class="Delimiter">{</span>
get<span class="Delimiter">(</span>Recipe<span class="Delimiter">,</span> r<span class="Delimiter">).</span>is_autogenerated = <span class="Constant">true</span><span class="Delimiter">;</span>
<span class="Delimiter">}</span>
<span class="Delimiter">:(after "void transform_all()")</span>
<span class="Normal">for</span> <span class="Delimiter">(</span>map<recipe_ordinal<span class="Delimiter">,</span> recipe>::iterator p = Recipe<span class="Delimiter">.</span>begin<span class="Delimiter">();</span> p != Recipe<span class="Delimiter">.</span>end<span class="Delimiter">();</span> ++p<span class="Delimiter">)</span> <span class="Delimiter">{</span>
<span class="Normal">const</span> recipe& r = p<span class="Delimiter">-></span>second<span class="Delimiter">;</span>
<span class="Normal">if</span> <span class="Delimiter">(</span>r<span class="Delimiter">.</span>name<span class="Delimiter">.</span>find<span class="Delimiter">(</span><span class="Constant">'_'</span><span class="Delimiter">)</span> == string::npos<span class="Delimiter">)</span> <span class="Identifier">continue</span><span class="Delimiter">;</span>
<span class="Normal">if</span> <span class="Delimiter">(</span>r<span class="Delimiter">.</span>is_autogenerated<span class="Delimiter">)</span> <span class="Identifier">continue</span><span class="Delimiter">;</span> <span class="Comment">// created by previous call to transform_all()</span>
raise << r<span class="Delimiter">.</span>name << <span class="Constant">": don't create recipes with '_' in the name</span><span class="cSpecial">\n</span><span class="Constant">"</span> << end<span class="Delimiter">();</span>
<span class="Delimiter">}</span>
<span class="SalientComment">//:: Helpers</span>
<span class="Delimiter">:(code)</span>
<span class="Comment">// just for the scenarios running scenarios in C++ layers</span>
<span class="Normal">void</span> run_mu_scenario<span class="Delimiter">(</span><span class="Normal">const</span> string& form<span class="Delimiter">)</span> <span class="Delimiter">{</span>
Scenario_names<span class="Delimiter">.</span>clear<span class="Delimiter">();</span>
istringstream in<span class="Delimiter">(</span>form<span class="Delimiter">);</span>
in >> std::noskipws<span class="Delimiter">;</span>
skip_whitespace_and_comments<span class="Delimiter">(</span>in<span class="Delimiter">);</span>
string _scenario = next_word<span class="Delimiter">(</span>in<span class="Delimiter">);</span>
assert<span class="Delimiter">(</span>_scenario == <span class="Constant">"scenario"</span><span class="Delimiter">);</span>
scenario s = parse_scenario<span class="Delimiter">(</span>in<span class="Delimiter">);</span>
run_mu_scenario<span class="Delimiter">(</span>s<span class="Delimiter">);</span>
<span class="Delimiter">}</span>
<span class="Normal">bool</span> starts_with<span class="Delimiter">(</span><span class="Normal">const</span> string& s<span class="Delimiter">,</span> <span class="Normal">const</span> string& pat<span class="Delimiter">)</span> <span class="Delimiter">{</span>
<span class="Identifier">return</span> s<span class="Delimiter">.</span>substr<span class="Delimiter">(</span><span class="Constant">0</span><span class="Delimiter">,</span> pat<span class="Delimiter">.</span>size<span class="Delimiter">())</span> == pat<span class="Delimiter">;</span>
<span class="Delimiter">}</span>
</pre>
</body>
</html>
<!-- vim: set foldmethod=manual : -->