hacks

(tidier examples of) random scripts from throughout the years
Log | Files | Refs | README

turing.sml (6790B)


      1 (* Editor's note: A random script I wrote for a Nature of Reason (80-150) assignment.
      2  * Written around spring 2024. The below introductory comment is taken from my submission:
      3  * 
      4  * I was procrastinating a bit last week and so I wrote up a little Standard ML program to 
      5  * simulate a Turing Machine!
      6  *
      7  * The bulk of the work is in representing the infinite tape of the Turing machine: I
      8  * considered having some wonky double-ended stream at first but I ended up just using a
      9  * function of type int -> bool to represent the state of the tape (thus to set index j to
     10  * true, you would redefine the existing function f to be fn i => i = j orelse f i). This
     11  * is pretty wasteful in terms of memory usage since it means that a Turing program that
     12  * just flips one cell forever would use increasing amounts of memory, but explicitly
     13  * tracking which cells have been flipped seem like a bunch of work.
     14  *
     15  * The actual Turing program evaluator is super simple: it just fetches the rules for the
     16  * current state from the program, applies them to the tape, and recurses (with the base
     17  * case being state 0).
     18  *
     19  * I almost wrote a parser for this too (did you know SMLNJ ships with a parser combinator
     20  * library?!), but some of the validation logic was icky and so I ran out of energy.
     21  * Final tidbit: the tape has to track some other metadata (current position, bounds of 1s
     22  * to read) and so I actually use a wackier feature of SML: flex records (look at the type
     23  * definition for tape! or try typing something like {x=2, y="whee"} into your SML
     24  * interpreter!).  They’re basically anonymous named tuples. Kinda cool feature which I
     25  * haven’t seen in any other language (to be fair they’re a bit limited: often SML will
     26  * complain if you try to write polymorphic functions that access fields of arbitrary flex
     27  * records). *)
     28 
     29 datatype direction = Left | Right
     30 type rule = (int * bool * direction)
     31 type instr = (rule * rule)
     32 
     33 signature TAPE = sig
     34     type tape
     35     val inpNum : int -> tape
     36     val inpPair : int*int -> tape
     37     val outNum : tape -> int
     38     val toList : tape -> bool list
     39     val get : tape -> bool
     40     val set : tape -> bool -> tape
     41     val move : tape -> direction -> tape
     42 end
     43                                          
     44 structure Tape :> TAPE = struct
     45 type tape = {bounds: (int * int), loc: int, f: int -> bool}
     46 fun inpNum x = {bounds=(0, x), loc=0, f=fn i => i >= 0 andalso i <= x}
     47 
     48 fun inpPair (x,y) =
     49     {bounds=(0, x+y+3), loc=0, f=(fn i => (i >= 0 andalso i <= x) orelse
     50                                           (i > x+1 andalso i <= x+2+y))}
     51 
     52 fun toList (t : tape) =
     53     let val (min,max) = #bounds t
     54     in List.tabulate (max-min, fn i => (#f t) (min+i)) end
     55                             
     56 fun outNum (t : tape) = let
     57     fun count (t : tape) i sum =
     58         if i > (#2 (#bounds t)) then sum
     59         else count t (i+1) (sum + (if ((#f t) i) then 1 else 0))
     60 in
     61     count t (#1 (#bounds t)) 0
     62 end
     63 
     64 fun get (t : tape) = (#f t) (#loc t)
     65 fun set (t : tape) new = let
     66     val (min, max) = #bounds t
     67     val min = Int.min(min, #loc t)
     68     val max = Int.max(max, #loc t)
     69 in
     70     {bounds=(min,max), loc=(#loc t),
     71      f=(fn i => if i = (#loc t) then new else (#f t) i)} 
     72 end
     73 fun move (t : tape) dir =
     74     let val shift = (case dir of Left => ~1 | Right => 1)
     75     in {bounds=(#bounds t), loc=(#loc t)+shift, f=(#f t)} end
     76 end 
     77 
     78 fun eval t 0 _ = t
     79   | eval (tape : Tape.tape) (state : int) (instrs : instr list) = let
     80     val ins = List.nth (instrs, state - 1)
     81     val (newstate, newval, dir) = if Tape.get tape then #2 ins else #1 ins
     82 in
     83     eval (Tape.move (Tape.set tape newval) dir) newstate instrs
     84 end
     85 
     86 (* I originally had a parser for this, but it ended up being too much of a
     87  * hassle (being able to leave off unreachable rules for some states and
     88  * other stuff made it annoying to parse). Instead programs are represented
     89  * as a list of instructions where the nth item represents the two rules
     90  * for the nth state (rule being what to do for a 0 and what to do for a 1. *)
     91 
     92 val plus3 = [((2, true, Right), (1, true, Right)),
     93              ((0, true, Right), (0, true, Right))]
     94 
     95 val ident = [((1, true, Right), (0, false, Right))]
     96 
     97 val three = [((2, true, Right), (2, true, Right)),
     98              ((3, true, Right), (3, true, Right)),
     99              ((4, true, Right), (4, true, Right)),
    100              ((0, false, Right), (4, false, Right))]
    101 
    102 val add = [((1, false, Right), (2, false, Right)),
    103            ((3, false, Right), (2, true, Right)),
    104            ((0, false, Right), (0, false, Right))]
    105 
    106 
    107 val mock = [((0, false, Right), (2, true, Right)),
    108             ((0, true, Right),  (3, false, Left)),
    109             ((0, false, Right), (0, false, Left))]
    110 
    111 (* Editor's note: The buried remnants of my attempt at a parser: *)
    112 			   
    113 (* structure P = ParserComb *)
    114 
    115 (* fun skipWS parser = P.skipBefore Char.isSpace parser *)
    116 (* (* Combines two parsers, ignoring whitespace. *) *)
    117 (* fun +> (a, b) = P.seq (a, skipWS(b)) *)
    118 (* infixr 3 +> *)
    119 (* (* Only keeps the right hand result. *) *)
    120 (* fun %> (a, b) = P.wrap(a +> b, #2) *)
    121 (* infixr 3 %> *)
    122 (* (* Only keeps the left hand result. *) *)
    123 (* fun @> (a, b) = P.wrap(a +> b, #1) *)
    124 (* infixr 3 @> *)
    125 
    126 (* (* Optionally applies a parser. On failure, parses to NONE instead of passing *)
    127 (*  * an actual failure up and causing the overall parse to fail. *) *)
    128 (* fun parseOptional parser getc = P.or (P.wrap (parser, SOME), P.result NONE) getc *)
    129          
    130 (* fun parseState getc = ((P.char #"q") %> (Int.scan StringCvt.DEC)) getc *)
    131 (* fun parseDir getc = P.or(P.wrap(P.char #"L", fn _ => Left), *)
    132 (*                          P.wrap(P.char #"R", fn _ => Right)) getc *)
    133 (* fun parseVal getc = P.or'([P.wrap(P.char #"B", fn _ => false), *)
    134 (*                            P.wrap(P.char #"0", fn _ => false), *)
    135 (*                            P.wrap(P.char #"1", fn _ => true)]) getc *)
    136          
    137 (* fun parseRule getc =  *)
    138 (*     P.wrap(parseState +> P.or'([P.wrap(P.char #"1", fn _ => true), *)
    139 (*                           P.wrap(P.char #"0", fn _ => false), *)
    140 (*                           P.wrap(P.char #"B", fn _ => false)])                            *)
    141 (*                       +> P.wrap(parseState +> parseVal +> parseDir, *)
    142 (*                                 fn (s, (v, d)) => (s,v,d)), *)
    143 (*           fn (s, (i, r)) => (s, i, r)) getc *)
    144 
    145 (* exception Parse *)
    146 (* fun strToRule str = Option.valOf (StringCvt.scanString parseRule str) *)
    147 (*                     handle Option => raise Parse *)
    148           
    149 (* fun parseRules rules = let *)
    150 (*     val rules = map strToRule rules *)
    151 (* in *)
    152     
    153 (* end *)
    154                     
    155 (* (* in *) *)
    156     
    157 (* (* end *) *)
    158                                            
    159 (* val prog = [ *)
    160 (*     "q3 1 q2 1 R", *)
    161 (*     "q3 0 q2 1 R", *)
    162 (*     "q2 1 q0 1 R" *)
    163 (* ] *)