summary refs log blame commit diff stats
path: root/compiler/vmmarshal.nim
blob: ffadab4f45323c44a453ab11097c22cbff60d2c3 (plain) (tree)
1
2
3
4
5
6
7
8
9
10
11










                                                   
                                                                         
                    






                                              

                                       

                              
                                     
                            

                       
                            
                                                  
                                
                   



                                                 
                                                                                     
 

                                                                                         
               
                         
                             
                 
                                  

                             

                                       
                                                   



                                     
                                              



                                  
                 
 

                                                                    












                                                                 
                         
                             
                                                   


                
                       


                           
                                                     


              
                                   


              
                       


                                 
                                               

                                           
                                                 

                      
                                                  
              

                                             

















                                                                     
                                             

                         
                                        



                                                       
                                                                                  
 
                                                                    
                           
                                 

                                         
                                               
                               
                                      
































                                                                              
                                                         










                                                         
                                                           






                                                                                
                                   






                                                                            
                                                   





                                                                                

                                     


                                                            
                                        
                                            


                                                                               
                                  
                           

                                              
                                                                            

                                                           






                                                                           
                                                        








                                                       
                                         







                                                                        
                                                        

























                                                                    
                                             
                                                    
       
                                                                          
 
                                                                               



                                             
                                          
          
#
#
#           The Nim Compiler
#        (c) Copyright 2015 Andreas Rumpf
#
#    See the file "copying.txt", included in this
#    distribution, for details about the copyright.
#

## Implements marshaling for the VM.

import streams, json, intsets, tables, ast, astalgo, idents, types, msgs,
  options, lineinfos

proc ptrToInt(x: PNode): int {.inline.} =
  result = cast[int](x) # don't skip alignment

proc getField(n: PNode; position: int): PSym =
  case n.kind
  of nkRecList:
    for i in 0..<n.len:
      result = getField(n[i], position)
      if result != nil: return
  of nkRecCase:
    result = getField(n[0], position)
    if result != nil: return
    for i in 1..<n.len:
      case n[i].kind
      of nkOfBranch, nkElse:
        result = getField(lastSon(n[i]), position)
        if result != nil: return
      else: discard
  of nkSym:
    if n.sym.position == position: result = n.sym
  else: discard

proc storeAny(s: var string; t: PType; a: PNode; stored: var IntSet; conf: ConfigRef)

proc storeObj(s: var string; typ: PType; x: PNode; stored: var IntSet; conf: ConfigRef) =
  assert x.kind == nkObjConstr
  let start = 1
  for i in start..<x.len:
    if i > start: s.add(", ")
    var it = x[i]
    if it.kind == nkExprColonExpr:
      if it[0].kind == nkSym:
        let field = it[0].sym
        s.add(escapeJson(field.name.s))
        s.add(": ")
        storeAny(s, field.typ, it[1], stored, conf)
    elif typ.n != nil:
      let field = getField(typ.n, i)
      s.add(escapeJson(field.name.s))
      s.add(": ")
      storeAny(s, field.typ, it, stored, conf)

proc skipColon*(n: PNode): PNode =
  result = n
  if n.kind == nkExprColonExpr:
    result = n[1]

proc storeAny(s: var string; t: PType; a: PNode; stored: var IntSet;
              conf: ConfigRef) =
  case t.kind
  of tyNone: assert false
  of tyBool: s.add($(a.intVal != 0))
  of tyChar:
    let ch = char(a.intVal)
    if ch < '\128':
      s.add(escapeJson($ch))
    else:
      s.add($int(ch))
  of tyArray, tySequence:
    if t.kind == tySequence and a.kind == nkNilLit: s.add("null")
    else:
      s.add("[")
      for i in 0..<a.len:
        if i > 0: s.add(", ")
        storeAny(s, t.elemType, a[i], stored, conf)
      s.add("]")
  of tyTuple:
    s.add("{")
    for i in 0..<t.len:
      if i > 0: s.add(", ")
      s.add("\"Field" & $i)
      s.add("\": ")
      storeAny(s, t[i], a[i].skipColon, stored, conf)
    s.add("}")
  of tyObject:
    s.add("{")
    storeObj(s, t, a, stored, conf)
    s.add("}")
  of tySet:
    s.add("[")
    for i in 0..<a.len:
      if i > 0: s.add(", ")
      if a[i].kind == nkRange:
        var x = copyNode(a[i][0])
        storeAny(s, t.lastSon, x, stored, conf)
        while x.intVal+1 <= a[i][1].intVal:
          s.add(", ")
          storeAny(s, t.lastSon, x, stored, conf)
          inc x.intVal
      else:
        storeAny(s, t.lastSon, a[i], stored, conf)
    s.add("]")
  of tyRange, tyGenericInst, tyAlias, tySink:
    storeAny(s, t.lastSon, a, stored, conf)
  of tyEnum:
    # we need a slow linear search because of enums with holes:
    for e in items(t.n):
      if e.sym.position == a.intVal:
        s.add e.sym.name.s.escapeJson
        break
  of tyPtr, tyRef:
    var x = a
    if isNil(x) or x.kind == nkNilLit: s.add("null")
    elif stored.containsOrIncl(x.ptrToInt):
      # already stored, so we simply write out the pointer as an int:
      s.add($x.ptrToInt)
    else:
      # else as a [value, key] pair:
      # (reversed order for convenient x[0] access!)
      s.add("[")
      s.add($x.ptrToInt)
      s.add(", ")
      storeAny(s, t.lastSon, a, stored, conf)
      s.add("]")
  of tyString, tyCString:
    if a.kind == nkNilLit: s.add("null")
    else: s.add(escapeJson(a.strVal))
  of tyInt..tyInt64, tyUInt..tyUInt64: s.add($a.intVal)
  of tyFloat..tyFloat128: s.add($a.floatVal)
  else:
    internalError conf, a.info, "cannot marshal at compile-time " & t.typeToString

proc storeAny*(s: var string; t: PType; a: PNode; conf: ConfigRef) =
  var stored = initIntSet()
  storeAny(s, t, a, stored, conf)

proc loadAny(p: var JsonParser, t: PType,
             tab: var Table[BiggestInt, PNode];
             cache: IdentCache;
             conf: ConfigRef): PNode =
  case t.kind
  of tyNone: assert false
  of tyBool:
    case p.kind
    of jsonFalse: result = newIntNode(nkIntLit, 0)
    of jsonTrue: result = newIntNode(nkIntLit, 1)
    else: raiseParseErr(p, "'true' or 'false' expected for a bool")
    next(p)
  of tyChar:
    if p.kind == jsonString:
      var x = p.str
      if x.len == 1:
        result = newIntNode(nkIntLit, ord(x[0]))
        next(p)
        return
    elif p.kind == jsonInt:
      result = newIntNode(nkIntLit, getInt(p))
      next(p)
      return
    raiseParseErr(p, "string of length 1 expected for a char")
  of tyEnum:
    if p.kind == jsonString:
      for e in items(t.n):
        if e.sym.name.s == p.str:
          result = newIntNode(nkIntLit, e.sym.position)
          next(p)
          return
    raiseParseErr(p, "string expected for an enum")
  of tyArray:
    if p.kind != jsonArrayStart: raiseParseErr(p, "'[' expected for an array")
    next(p)
    result = newNode(nkBracket)
    while p.kind != jsonArrayEnd and p.kind != jsonEof:
      result.add loadAny(p, t.elemType, tab, cache, conf)
    if p.kind == jsonArrayEnd: next(p)
    else: raiseParseErr(p, "']' end of array expected")
  of tySequence:
    case p.kind
    of jsonNull:
      result = newNode(nkNilLit)
      next(p)
    of jsonArrayStart:
      next(p)
      result = newNode(nkBracket)
      while p.kind != jsonArrayEnd and p.kind != jsonEof:
        result.add loadAny(p, t.elemType, tab, cache, conf)
      if p.kind == jsonArrayEnd: next(p)
      else: raiseParseErr(p, "")
    else:
      raiseParseErr(p, "'[' expected for a seq")
  of tyTuple:
    if p.kind != jsonObjectStart: raiseParseErr(p, "'{' expected for an object")
    next(p)
    result = newNode(nkTupleConstr)
    var i = 0
    while p.kind != jsonObjectEnd and p.kind != jsonEof:
      if p.kind != jsonString:
        raiseParseErr(p, "string expected for a field name")
      next(p)
      if i >= t.len:
        raiseParseErr(p, "too many fields to tuple type " & typeToString(t))
      result.add loadAny(p, t[i], tab, cache, conf)
      inc i
    if p.kind == jsonObjectEnd: next(p)
    else: raiseParseErr(p, "'}' end of object expected")
  of tyObject:
    if p.kind != jsonObjectStart: raiseParseErr(p, "'{' expected for an object")
    next(p)
    result = newNode(nkObjConstr)
    result.sons = @[newNode(nkEmpty)]
    while p.kind != jsonObjectEnd and p.kind != jsonEof:
      if p.kind != jsonString:
        raiseParseErr(p, "string expected for a field name")
      let ident = getIdent(cache, p.str)
      let field = lookupInRecord(t.n, ident)
      if field.isNil:
        raiseParseErr(p, "unknown field for object of type " & typeToString(t))
      next(p)
      let pos = field.position + 1
      if pos >= result.len:
        setLen(result.sons, pos + 1)
      let fieldNode = newNode(nkExprColonExpr)
      fieldNode.add newSymNode(newSym(skField, ident, nil, unknownLineInfo))
      fieldNode.add loadAny(p, field.typ, tab, cache, conf)
      result[pos] = fieldNode
    if p.kind == jsonObjectEnd: next(p)
    else: raiseParseErr(p, "'}' end of object expected")
  of tySet:
    if p.kind != jsonArrayStart: raiseParseErr(p, "'[' expected for a set")
    next(p)
    result = newNode(nkCurly)
    while p.kind != jsonArrayEnd and p.kind != jsonEof:
      result.add loadAny(p, t.lastSon, tab, cache, conf)
      next(p)
    if p.kind == jsonArrayEnd: next(p)
    else: raiseParseErr(p, "']' end of array expected")
  of tyPtr, tyRef:
    case p.kind
    of jsonNull:
      result = newNode(nkNilLit)
      next(p)
    of jsonInt:
      result = tab.getOrDefault(p.getInt)
      if result.isNil:
        raiseParseErr(p, "cannot load object with address " & $p.getInt)
      next(p)
    of jsonArrayStart:
      next(p)
      if p.kind == jsonInt:
        let idx = p.getInt
        next(p)
        result = loadAny(p, t.lastSon, tab, cache, conf)
        tab[idx] = result
      else: raiseParseErr(p, "index for ref type expected")
      if p.kind == jsonArrayEnd: next(p)
      else: raiseParseErr(p, "']' end of ref-address pair expected")
    else: raiseParseErr(p, "int for pointer type expected")
  of tyString, tyCString:
    case p.kind
    of jsonNull:
      result = newNode(nkNilLit)
      next(p)
    of jsonString:
      result = newStrNode(nkStrLit, p.str)
      next(p)
    else: raiseParseErr(p, "string expected")
  of tyInt..tyInt64, tyUInt..tyUInt64:
    if p.kind == jsonInt:
      result = newIntNode(nkIntLit, getInt(p))
      next(p)
      return
    raiseParseErr(p, "int expected")
  of tyFloat..tyFloat128:
    if p.kind == jsonFloat:
      result = newFloatNode(nkFloatLit, getFloat(p))
      next(p)
      return
    raiseParseErr(p, "float expected")
  of tyRange, tyGenericInst, tyAlias, tySink:
    result = loadAny(p, t.lastSon, tab, cache, conf)
  else:
    internalError conf, "cannot marshal at compile-time " & t.typeToString

proc loadAny*(s: string; t: PType; cache: IdentCache; conf: ConfigRef): PNode =
  var tab = initTable[BiggestInt, PNode]()
  var p: JsonParser
  open(p, newStringStream(s), "unknown file")
  next(p)
  result = loadAny(p, t, tab, cache, conf)
  close(p)
lass="nt">span class="Normal">bool</span> Trace_main = <span class="Constant">false</span><span class="Delimiter">;</span> <span class="Delimiter">:(before &quot;End Commandline Options(*arg)&quot;)</span> <span class="Normal">else</span> <span class="Normal">if</span> <span class="Delimiter">(</span>is_equal<span class="Delimiter">(</span>*arg<span class="Delimiter">,</span> <span class="Constant">&quot;--trace&quot;</span><span class="Delimiter">))</span> <span class="Delimiter">{</span> Trace_main = <span class="Constant">true</span><span class="Delimiter">;</span> <span class="Delimiter">}</span> <span class="Delimiter">:(code)</span> <span class="Normal">void</span> dump_profile<span class="Delimiter">()</span> <span class="Delimiter">{</span> <span class="Normal">for</span> <span class="Delimiter">(</span>map&lt;string<span class="Delimiter">,</span> <span class="Normal">int</span>&gt;::iterator p = Instructions_running<span class="Delimiter">.</span>begin<span class="Delimiter">();</span> p != Instructions_running<span class="Delimiter">.</span>end<span class="Delimiter">();</span> ++p<span class="Delimiter">)</span> <span class="Delimiter">{</span> cerr &lt;&lt; p<span class="Delimiter">-&gt;</span>first &lt;&lt; <span class="Constant">&quot;: &quot;</span> &lt;&lt; p<span class="Delimiter">-&gt;</span>second &lt;&lt; <span class="cSpecial">'\n'</span><span class="Delimiter">;</span> <span class="Delimiter">}</span> cerr &lt;&lt; <span class="Constant">&quot;== locations read</span><span class="cSpecial">\n</span><span class="Constant">&quot;</span><span class="Delimiter">;</span> <span class="Normal">for</span> <span class="Delimiter">(</span>map&lt;string<span class="Delimiter">,</span> <span class="Normal">int</span>&gt;::iterator p = Locations_read<span class="Delimiter">.</span>begin<span class="Delimiter">();</span> p != Locations_read<span class="Delimiter">.</span>end<span class="Delimiter">();</span> ++p<span class="Delimiter">)</span> <span class="Delimiter">{</span> cerr &lt;&lt; p<span class="Delimiter">-&gt;</span>first &lt;&lt; <span class="Constant">&quot;: &quot;</span> &lt;&lt; p<span class="Delimiter">-&gt;</span>second &lt;&lt; <span class="cSpecial">'\n'</span><span class="Delimiter">;</span> <span class="Delimiter">}</span> cerr &lt;&lt; <span class="Constant">&quot;== locations read by instruction</span><span class="cSpecial">\n</span><span class="Constant">&quot;</span><span class="Delimiter">;</span> <span class="Normal">for</span> <span class="Delimiter">(</span>map&lt;string<span class="Delimiter">,</span> <span class="Normal">int</span>&gt;::iterator p = Locations_read_by_instruction<span class="Delimiter">.</span>begin<span class="Delimiter">();</span> p != Locations_read_by_instruction<span class="Delimiter">.</span>end<span class="Delimiter">();</span> ++p<span class="Delimiter">)</span> <span class="Delimiter">{</span> cerr &lt;&lt; p<span class="Delimiter">-&gt;</span>first &lt;&lt; <span class="Constant">&quot;: &quot;</span> &lt;&lt; p<span class="Delimiter">-&gt;</span>second &lt;&lt; <span class="cSpecial">'\n'</span><span class="Delimiter">;</span> <span class="Delimiter">}</span> <span class="Delimiter">}</span> <span class="Delimiter">:(before &quot;End One-time Setup&quot;)</span> <span class="CommentedCode">//? atexit(dump_profile);</span> <span class="Delimiter">:(code)</span> <span class="Normal">void</span> cleanup_main<span class="Delimiter">()</span> <span class="Delimiter">{</span> <span class="Normal">if</span> <span class="Delimiter">(</span>Save_trace &amp;&amp; Trace_stream<span class="Delimiter">)</span> <span class="Delimiter">{</span> ofstream fout<span class="Delimiter">(</span><span class="Constant">&quot;interactive&quot;</span><span class="Delimiter">);</span> fout &lt;&lt; Trace_stream<span class="Delimiter">-&gt;</span>readable_contents<span class="Delimiter">(</span><span class="Constant">&quot;&quot;</span><span class="Delimiter">);</span> fout<span class="Delimiter">.</span>close<span class="Delimiter">();</span> <span class="Delimiter">}</span> <span class="Normal">if</span> <span class="Delimiter">(</span>Trace_stream<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> <span class="Delimiter">:(before &quot;End One-time Setup&quot;)</span> atexit<span class="Delimiter">(</span>cleanup_main<span class="Delimiter">);</span> <span class="Delimiter">:(code)</span> <span class="Normal">void</span> load_file_or_directory<span class="Delimiter">(</span>string filename<span class="Delimiter">)</span> <span class="Delimiter">{</span> <span class="Normal">if</span> <span class="Delimiter">(</span>is_directory<span class="Delimiter">(</span>filename<span class="Delimiter">))</span> <span class="Delimiter">{</span> load_all<span class="Delimiter">(</span>filename<span class="Delimiter">);</span> <span class="Identifier">return</span><span class="Delimiter">;</span> <span class="Delimiter">}</span> ifstream fin<span class="Delimiter">(</span>filename<span class="Delimiter">.</span>c_str<span class="Delimiter">());</span> <span class="Normal">if</span> <span class="Delimiter">(</span>!fin<span class="Delimiter">)</span> <span class="Delimiter">{</span> cerr &lt;&lt; <span class="Constant">&quot;no such file '&quot;</span> &lt;&lt; filename &lt;&lt; <span class="Constant">&quot;'</span><span class="cSpecial">\n</span><span class="Constant">&quot;</span> &lt;&lt; end<span class="Delimiter">();</span> <span class="Comment">// don't raise, just warn. just in case it's just a name for a scenario to run.</span> <span class="Identifier">return</span><span class="Delimiter">;</span> <span class="Delimiter">}</span> trace<span class="Delimiter">(</span><span class="Constant">9990</span><span class="Delimiter">,</span> <span class="Constant">&quot;load&quot;</span><span class="Delimiter">)</span> &lt;&lt; <span class="Constant">&quot;=== &quot;</span> &lt;&lt; filename &lt;&lt; end<span class="Delimiter">();</span> load<span class="Delimiter">(</span>fin<span class="Delimiter">);</span> fin<span class="Delimiter">.</span>close<span class="Delimiter">();</span> <span class="Delimiter">}</span> <span class="Normal">bool</span> is_directory<span class="Delimiter">(</span>string path<span class="Delimiter">)</span> <span class="Delimiter">{</span> <span class="Normal">struct</span> stat info<span class="Delimiter">;</span> <span class="Normal">if</span> <span class="Delimiter">(</span>stat<span class="Delimiter">(</span>path<span class="Delimiter">.</span>c_str<span class="Delimiter">(),</span> &amp;info<span class="Delimiter">))</span> <span class="Identifier">return</span> <span class="Constant">false</span><span class="Delimiter">;</span> <span class="Comment">// error</span> <span class="Identifier">return</span> info<span class="Delimiter">.</span>st_mode &amp; S_IFDIR<span class="Delimiter">;</span> <span class="Delimiter">}</span> <span class="Normal">void</span> load_all<span class="Delimiter">(</span>string dir<span class="Delimiter">)</span> <span class="Delimiter">{</span> dirent** files<span class="Delimiter">;</span> <span class="Normal">int</span> num_files = scandir<span class="Delimiter">(</span>dir<span class="Delimiter">.</span>c_str<span class="Delimiter">(),</span> &amp;files<span class="Delimiter">,</span> <span class="Constant">NULL</span><span class="Delimiter">,</span> alphasort<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 &lt; num_files<span class="Delimiter">;</span> ++i<span class="Delimiter">)</span> <span class="Delimiter">{</span> string curr_file = files[i]<span class="Delimiter">-&gt;</span>d_name<span class="Delimiter">;</span> <span class="Normal">if</span> <span class="Delimiter">(</span>isdigit<span class="Delimiter">(</span>curr_file<span class="Delimiter">.</span>at<span class="Delimiter">(</span><span class="Constant">0</span><span class="Delimiter">)))</span> load_file_or_directory<span class="Delimiter">(</span>dir+<span class="Constant">'/'</span>+curr_file<span class="Delimiter">);</span> free<span class="Delimiter">(</span>files[i]<span class="Delimiter">);</span> files[i] = <span class="Constant">NULL</span><span class="Delimiter">;</span> <span class="Delimiter">}</span> free<span class="Delimiter">(</span>files<span class="Delimiter">);</span> <span class="Delimiter">}</span> <span class="Delimiter">:(before &quot;End Includes&quot;)</span> <span class="PreProc">#include </span><span class="Constant">&lt;dirent.h&gt;</span> <span class="PreProc">#include </span><span class="Constant">&lt;sys/stat.h&gt;</span> <span class="SalientComment">//:: Reading from memory, writing to memory.</span> <span class="Delimiter">:(code)</span> vector&lt;<span class="Normal">double</span>&gt; read_memory<span class="Delimiter">(</span>reagent<span class="Comment">/*</span><span class="Comment">copy</span><span class="Comment">*/</span> x<span class="Delimiter">)</span> <span class="Delimiter">{</span> <span class="Comment">// Begin Preprocess read_memory(x)</span> vector&lt;<span class="Normal">double</span>&gt; result<span class="Delimiter">;</span> <span class="Normal">if</span> <span class="Delimiter">(</span>is_literal<span class="Delimiter">(</span>x<span class="Delimiter">))</span> <span class="Delimiter">{</span> result<span class="Delimiter">.</span>push_back<span class="Delimiter">(</span>x<span class="Delimiter">.</span>value<span class="Delimiter">);</span> <span class="Identifier">return</span> result<span class="Delimiter">;</span> <span class="Delimiter">}</span> <span class="Comment">// End Preprocess read_memory(x)</span> <span class="Normal">int</span> size = size_of<span class="Delimiter">(</span>x<span class="Delimiter">);</span> <span class="Normal">for</span> <span class="Delimiter">(</span><span class="Normal">int</span> offset = <span class="Constant">0</span><span class="Delimiter">;</span> offset &lt; size<span class="Delimiter">;</span> ++offset<span class="Delimiter">)</span> <span class="Delimiter">{</span> <span class="Normal">double</span> val = get_or_insert<span class="Delimiter">(</span>Memory<span class="Delimiter">,</span> x<span class="Delimiter">.</span>value+offset<span class="Delimiter">);</span> trace<span class="Delimiter">(</span><span class="Constant">9999</span><span class="Delimiter">,</span> <span class="Constant">&quot;mem&quot;</span><span class="Delimiter">)</span> &lt;&lt; <span class="Constant">&quot;location &quot;</span> &lt;&lt; x<span class="Delimiter">.</span>value+offset &lt;&lt; <span class="Constant">&quot; is &quot;</span> &lt;&lt; no_scientific<span class="Delimiter">(</span>val<span class="Delimiter">)</span> &lt;&lt; end<span class="Delimiter">();</span> result<span class="Delimiter">.</span>push_back<span class="Delimiter">(</span>val<span class="Delimiter">);</span> <span class="Delimiter">}</span> <span class="Identifier">return</span> result<span class="Delimiter">;</span> <span class="Delimiter">}</span> <span class="Normal">void</span> write_memory<span class="Delimiter">(</span>reagent<span class="Comment">/*</span><span class="Comment">copy</span><span class="Comment">*/</span> x<span class="Delimiter">,</span> <span class="Normal">const</span> vector&lt;<span class="Normal">double</span>&gt;&amp; data<span class="Delimiter">)</span> <span class="Delimiter">{</span> assert<span class="Delimiter">(</span>Current_routine<span class="Delimiter">);</span> <span class="Comment">// run-time only</span> <span class="Comment">// Begin Preprocess write_memory(x, data)</span> <span class="Normal">if</span> <span class="Delimiter">(</span>!x<span class="Delimiter">.</span>type<span class="Delimiter">)</span> <span class="Delimiter">{</span> raise &lt;&lt; <span class="Constant">&quot;can't write to '&quot;</span> &lt;&lt; to_string<span class="Delimiter">(</span>x<span class="Delimiter">)</span> &lt;&lt; <span class="Constant">&quot;'; no type</span><span class="cSpecial">\n</span><span class="Constant">&quot;</span> &lt;&lt; end<span class="Delimiter">();</span> <span class="Identifier">return</span><span class="Delimiter">;</span> <span class="Delimiter">}</span> <span class="Normal">if</span> <span class="Delimiter">(</span>is_dummy<span class="Delimiter">(</span>x<span class="Delimiter">))</span> <span class="Identifier">return</span><span class="Delimiter">;</span> <span class="Normal">if</span> <span class="Delimiter">(</span>is_literal<span class="Delimiter">(</span>x<span class="Delimiter">))</span> <span class="Identifier">return</span><span class="Delimiter">;</span> <span class="Comment">// End Preprocess write_memory(x, data)</span> <span class="Normal">if</span> <span class="Delimiter">(</span>x<span class="Delimiter">.</span>value == <span class="Constant">0</span><span class="Delimiter">)</span> <span class="Delimiter">{</span> raise &lt;&lt; <span class="Constant">&quot;can't write to location 0 in '&quot;</span> &lt;&lt; to_original_string<span class="Delimiter">(</span>current_instruction<span class="Delimiter">())</span> &lt;&lt; <span class="Constant">&quot;'</span><span class="cSpecial">\n</span><span class="Constant">&quot;</span> &lt;&lt; end<span class="Delimiter">();</span> <span class="Identifier">return</span><span class="Delimiter">;</span> <span class="Delimiter">}</span> <span class="Normal">if</span> <span class="Delimiter">(</span>size_mismatch<span class="Delimiter">(</span>x<span class="Delimiter">,</span> data<span class="Delimiter">))</span> <span class="Delimiter">{</span> raise &lt;&lt; maybe<span class="Delimiter">(</span>current_recipe_name<span class="Delimiter">())</span> &lt;&lt; <span class="Constant">&quot;size mismatch in storing to '&quot;</span> &lt;&lt; x<span class="Delimiter">.</span>original_string &lt;&lt; <span class="Constant">&quot;' (&quot;</span> &lt;&lt; size_of<span class="Delimiter">(</span>x<span class="Delimiter">)</span> &lt;&lt; <span class="Constant">&quot; vs &quot;</span> &lt;&lt; SIZE<span class="Delimiter">(</span>data<span class="Delimiter">)</span> &lt;&lt; <span class="Constant">&quot;) at '&quot;</span> &lt;&lt; to_original_string<span class="Delimiter">(</span>current_instruction<span class="Delimiter">())</span> &lt;&lt; <span class="Constant">&quot;'</span><span class="cSpecial">\n</span><span class="Constant">&quot;</span> &lt;&lt; end<span class="Delimiter">();</span> <span class="Identifier">return</span><span class="Delimiter">;</span> <span class="Delimiter">}</span> <span class="Comment">// End write_memory(x) Special-cases</span> <span class="Normal">for</span> <span class="Delimiter">(</span><span class="Normal">int</span> offset = <span class="Constant">0</span><span class="Delimiter">;</span> offset &lt; SIZE<span class="Delimiter">(</span>data<span class="Delimiter">);</span> ++offset<span class="Delimiter">)</span> <span class="Delimiter">{</span> assert<span class="Delimiter">(</span>x<span class="Delimiter">.</span>value+offset &gt; <span class="Constant">0</span><span class="Delimiter">);</span> trace<span class="Delimiter">(</span><span class="Constant">9999</span><span class="Delimiter">,</span> <span class="Constant">&quot;mem&quot;</span><span class="Delimiter">)</span> &lt;&lt; <span class="Constant">&quot;storing &quot;</span> &lt;&lt; no_scientific<span class="Delimiter">(</span>data<span class="Delimiter">.</span>at<span class="Delimiter">(</span>offset<span class="Delimiter">))</span> &lt;&lt; <span class="Constant">&quot; in location &quot;</span> &lt;&lt; x<span class="Delimiter">.</span>value+offset &lt;&lt; end<span class="Delimiter">();</span> put<span class="Delimiter">(</span>Memory<span class="Delimiter">,</span> x<span class="Delimiter">.</span>value+offset<span class="Delimiter">,</span> data<span class="Delimiter">.</span>at<span class="Delimiter">(</span>offset<span class="Delimiter">));</span> <span class="Delimiter">}</span> <span class="Delimiter">}</span> <span class="Delimiter">:(code)</span> <span class="Normal">int</span> size_of<span class="Delimiter">(</span><span class="Normal">const</span> reagent&amp; r<span class="Delimiter">)</span> <span class="Delimiter">{</span> <span class="Normal">if</span> <span class="Delimiter">(</span>!r<span class="Delimiter">.</span>type<span class="Delimiter">)</span> <span class="Identifier">return</span> <span class="Constant">0</span><span class="Delimiter">;</span> <span class="Comment">// End size_of(reagent r) Cases</span> <span class="Identifier">return</span> size_of<span class="Delimiter">(</span>r<span class="Delimiter">.</span>type<span class="Delimiter">);</span> <span class="Delimiter">}</span> <span class="Normal">int</span> size_of<span class="Delimiter">(</span><span class="Normal">const</span> type_tree* type<span class="Delimiter">)</span> <span class="Delimiter">{</span> <span class="Normal">if</span> <span class="Delimiter">(</span>!type<span class="Delimiter">)</span> <span class="Identifier">return</span> <span class="Constant">0</span><span class="Delimiter">;</span> <span class="Comment">// End size_of(type) Cases</span> <span class="Identifier">return</span> <span class="Constant">1</span><span class="Delimiter">;</span> <span class="Delimiter">}</span> <span class="Normal">bool</span> size_mismatch<span class="Delimiter">(</span><span class="Normal">const</span> reagent&amp; x<span class="Delimiter">,</span> <span class="Normal">const</span> vector&lt;<span class="Normal">double</span>&gt;&amp; data<span class="Delimiter">)</span> <span class="Delimiter">{</span> <span class="Normal">if</span> <span class="Delimiter">(</span>!x<span class="Delimiter">.</span>type<span class="Delimiter">)</span> <span class="Identifier">return</span> <span class="Constant">true</span><span class="Delimiter">;</span> <span class="Comment">// End size_mismatch(x) Cases</span> <span class="CommentedCode">//? if (size_of(x) != SIZE(data)) cerr &lt;&lt; size_of(x) &lt;&lt; &quot; vs &quot; &lt;&lt; SIZE(data) &lt;&lt; '\n';</span> <span class="Identifier">return</span> size_of<span class="Delimiter">(</span>x<span class="Delimiter">)</span> != SIZE<span class="Delimiter">(</span>data<span class="Delimiter">);</span> <span class="Delimiter">}</span> <span class="Normal">bool</span> is_literal<span class="Delimiter">(</span><span class="Normal">const</span> reagent&amp; r<span class="Delimiter">)</span> <span class="Delimiter">{</span> <span class="Identifier">return</span> is_literal<span class="Delimiter">(</span>r<span class="Delimiter">.</span>type<span class="Delimiter">);</span> <span class="Delimiter">}</span> <span class="Normal">bool</span> is_literal<span class="Delimiter">(</span><span class="Normal">const</span> type_tree* type<span class="Delimiter">)</span> <span class="Delimiter">{</span> <span class="Normal">if</span> <span class="Delimiter">(</span>!type<span class="Delimiter">)</span> <span class="Identifier">return</span> <span class="Constant">false</span><span class="Delimiter">;</span> <span class="Normal">if</span> <span class="Delimiter">(</span>!type<span class="Delimiter">-&gt;</span>atom<span class="Delimiter">)</span> <span class="Identifier">return</span> <span class="Constant">false</span><span class="Delimiter">;</span> <span class="Identifier">return</span> type<span class="Delimiter">-&gt;</span>value == <span class="Constant">0</span><span class="Delimiter">;</span> <span class="Delimiter">}</span> <span class="Normal">bool</span> scalar<span class="Delimiter">(</span><span class="Normal">const</span> vector&lt;<span class="Normal">int</span>&gt;&amp; x<span class="Delimiter">)</span> <span class="Delimiter">{</span> <span class="Identifier">return</span> SIZE<span class="Delimiter">(</span>x<span class="Delimiter">)</span> == <span class="Constant">1</span><span class="Delimiter">;</span> <span class="Delimiter">}</span> <span class="Normal">bool</span> scalar<span class="Delimiter">(</span><span class="Normal">const</span> vector&lt;<span class="Normal">double</span>&gt;&amp; x<span class="Delimiter">)</span> <span class="Delimiter">{</span> <span class="Identifier">return</span> SIZE<span class="Delimiter">(</span>x<span class="Delimiter">)</span> == <span class="Constant">1</span><span class="Delimiter">;</span> <span class="Delimiter">}</span> <span class="Comment">// helper for tests</span> <span class="Normal">void</span> run<span class="Delimiter">(</span><span class="Normal">const</span> string&amp; form<span class="Delimiter">)</span> <span class="Delimiter">{</span> vector&lt;recipe_ordinal&gt; tmp = load<span class="Delimiter">(</span>form<span class="Delimiter">);</span> transform_all<span class="Delimiter">();</span> <span class="Normal">if</span> <span class="Delimiter">(</span>tmp<span class="Delimiter">.</span>empty<span class="Delimiter">())</span> <span class="Identifier">return</span><span class="Delimiter">;</span> <span class="Normal">if</span> <span class="Delimiter">(</span>trace_count<span class="Delimiter">(</span><span class="Constant">&quot;error&quot;</span><span class="Delimiter">)</span> &gt; <span class="Constant">0</span><span class="Delimiter">)</span> <span class="Identifier">return</span><span class="Delimiter">;</span> <span class="Comment">// if a test defines main, it probably wants to start there regardless of</span> <span class="Comment">// definition order</span> <span class="Normal">if</span> <span class="Delimiter">(</span>contains_key<span class="Delimiter">(</span>Recipe<span class="Delimiter">,</span> get<span class="Delimiter">(</span>Recipe_ordinal<span class="Delimiter">,</span> <span class="Constant">&quot;main&quot;</span><span class="Delimiter">)))</span> run<span class="Delimiter">(</span>get<span class="Delimiter">(</span>Recipe_ordinal<span class="Delimiter">,</span> <span class="Constant">&quot;main&quot;</span><span class="Delimiter">));</span> <span class="Normal">else</span> run<span class="Delimiter">(</span>tmp<span class="Delimiter">.</span>front<span class="Delimiter">());</span> <span class="Delimiter">}</span> <span class="Delimiter">:(scenario run_label)</span> <span class="muRecipe">def</span> main [ +foo <span class="Constant">1</span>:num<span class="Special"> &lt;- </span>copy <span class="Constant">23</span> <span class="Constant">2</span>:num<span class="Special"> &lt;- </span>copy <span class="Constant">1</span>:num ] <span class="traceContains">+run: {1: &quot;number&quot;} &lt;- copy {23: &quot;literal&quot;}</span> <span class="traceContains">+run: {2: &quot;number&quot;} &lt;- copy {1: &quot;number&quot;}</span> <span class="traceAbsent">-run: +foo</span> <span class="Delimiter">:(scenario run_dummy)</span> <span class="muRecipe">def</span> main [ _<span class="Special"> &lt;- </span>copy <span class="Constant">0</span> ] <span class="traceContains">+run: _ &lt;- copy {0: &quot;literal&quot;}</span> <span class="Delimiter">:(scenario write_to_0_disallowed)</span> <span class="Special">% Hide_errors = true;</span> <span class="muRecipe">def</span> main [ <span class="Constant">0</span>:num<span class="Special"> &lt;- </span>copy <span class="Constant">34</span> ] <span class="traceAbsent">-mem: storing 34 in location 0</span> <span class="Comment">//: Mu is robust to various combinations of commas and spaces. You just have</span> <span class="Comment">//: to put spaces around the '&lt;-'.</span> <span class="Delimiter">:(scenario comma_without_space)</span> <span class="muRecipe">def</span> main [ <span class="Constant">1</span>:num<span class="Delimiter">,</span> <span class="Constant">2</span>:num<span class="Special"> &lt;- </span>copy <span class="Constant">2</span><span class="Delimiter">,</span><span class="Constant">2</span> ] <span class="traceContains">+mem: storing 2 in location 1</span> <span class="Delimiter">:(scenario space_without_comma)</span> <span class="muRecipe">def</span> main [ <span class="Constant">1</span>:num<span class="Delimiter">,</span> <span class="Constant">2</span>:num<span class="Special"> &lt;- </span>copy <span class="Constant">2</span> <span class="Constant">2</span> ] <span class="traceContains">+mem: storing 2 in location 1</span> <span class="Delimiter">:(scenario comma_before_space)</span> <span class="muRecipe">def</span> main [ <span class="Constant">1</span>:num<span class="Delimiter">,</span> <span class="Constant">2</span>:num<span class="Special"> &lt;- </span>copy <span class="Constant">2</span><span class="Delimiter">,</span> <span class="Constant">2</span> ] <span class="traceContains">+mem: storing 2 in location 1</span> <span class="Delimiter">:(scenario comma_after_space)</span> <span class="muRecipe">def</span> main [ <span class="Constant">1</span>:num<span class="Delimiter">,</span> <span class="Constant">2</span>:num<span class="Special"> &lt;- </span>copy <span class="Constant">2</span> <span class="Delimiter">,</span><span class="Constant">2</span> ] <span class="traceContains">+mem: storing 2 in location 1</span> </pre> </body> </html> <!-- vim: set foldmethod=manual : -->