Guest User

brainfuck-optim.ml

a guest
Apr 22nd, 2012
109
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
OCaml 8.09 KB | None | 0 0
  1. (** The abstract syntax tree (AST) that will be build then executed *)
  2. module ParseTree = struct
  3.   type operation =
  4.     | Move of int            (** add n to the cell pointer *)
  5.     | Add of int             (** add n to the current cell's value *)
  6.     | Output                 (** write the current cell as an ascii char *)
  7.     | Input                  (** read a char and write it in the current cell *)
  8.     | Loop of operation list (** loop while the current cell is <> 0 *)
  9.     (* following are optimizations *)
  10.     | Reset                  (** reset the current cell to 0 *)
  11.     | AddMultToCell of int * int(** add (current cell value)*n to the cell distant of i, then reset the current cell *)
  12.     | AddMultToCell2 of int * int * int (** add (curval)*n to the both cells distant of i and j, then reset current *)
  13.     | CopyMultToCell of int * int (** add curval*n to the cell distant of i, don't reset current *)
  14.     | AddTo of int * int (** add n to the cell distant of i, without moving *)
  15.  
  16.   let string_of_op ops =
  17.     let open Printf in
  18.     let rec to_string indent = function
  19.       | Move i -> sprintf "%sMove(%d)" indent i
  20.       | Add i -> sprintf "%sAdd(%d)" indent i
  21.       | Output -> sprintf "%sOutput" indent
  22.       | Input -> sprintf "%sInput" indent
  23.       | Loop nodes ->
  24.           sprintf "%sLoop <<\n%s\n%sLoop >>"
  25.             indent
  26.             (String.concat "\n" (List.map (to_string ("| "^indent)) nodes))
  27.             indent
  28.       | Reset -> sprintf "%sReset" indent
  29.       | AddMultToCell (n, i) -> sprintf "%sAddMultToCell(%d, %d)" indent n i
  30.       | AddMultToCell2 (n, i, j) -> sprintf "%sAddMultToCell2(%d, %d, %d)" indent n i j
  31.       | CopyMultToCell (n, i) -> sprintf "%sCopyMultToCell(%d, %d)" indent n i
  32.       | AddTo (n, i) -> sprintf "%sAddTo(%d, %d)" indent n i
  33.     in to_string "" ops
  34.  
  35.   let dump ast =
  36.     List.iter (fun op -> print_endline (string_of_op op)) ast
  37. end
  38.  
  39. (** Parse the source file and build the AST *)
  40. module Parser = struct
  41.   open ParseTree
  42.  
  43.   (* lexical analysis, build token list from chars *)
  44.  
  45.   type token =
  46.     | IncrPtr | DecrPtr
  47.     | IncrData | DecrData
  48.     | Write | Read
  49.     | Open | Close
  50.     | Comment of char
  51.  
  52.   let token_of_char = function
  53.     | '>' -> IncrPtr
  54.     | '<' -> DecrPtr
  55.     | '+' -> IncrData
  56.     | '-' -> DecrData
  57.     | '.' -> Write
  58.     | ',' -> Read
  59.     | '[' -> Open
  60.     | ']' -> Close
  61.     | c -> Comment c
  62.  
  63.   let tokenize charstream =
  64.     let tokens = ref [] in
  65.     Stream.iter (fun c -> tokens := (token_of_char c) :: !tokens) charstream;
  66.     List.rev !tokens
  67.  
  68.   (* syntaxic analysis, build AST from tokens *)
  69.  
  70.   let build_loop tokens =
  71.     let rec loop acc opened = function
  72.       | Open :: rest -> loop (Open :: acc) (succ opened) rest
  73.       | Close :: rest ->
  74.           if opened = 0 then (List.rev acc), rest
  75.           else loop (Close :: acc) (pred opened) rest
  76.       | other:: rest -> loop (other :: acc) opened rest
  77.       | [] -> failwith "malformed Loop"
  78.     in
  79.     loop [] 0 (List.tl tokens)
  80.  
  81.   let rec build_ast tokens =
  82.     let rec loop tokens acc =
  83.       match tokens with
  84.       | [] -> acc
  85.       | IncrPtr :: rest -> loop rest (Move 1 :: acc)
  86.       | DecrPtr :: rest -> loop rest (Move (-1) :: acc)
  87.       | IncrData :: rest -> loop rest (Add 1 :: acc)
  88.       | DecrData :: rest -> loop rest (Add (-1) :: acc)
  89.       | Write :: rest -> loop rest (Output :: acc)
  90.       | Read :: rest -> loop rest (Input :: acc)
  91.       | Open :: rest ->
  92.           let sublist, rest = build_loop tokens in
  93.           let cond = build_ast sublist in
  94.           loop rest ((Loop cond) :: acc)
  95.       | Close :: rest -> failwith "Close should have been consumed by build_loop"
  96.       | (Comment _) :: rest -> loop rest acc
  97.     in
  98.     List.rev (loop tokens [])
  99.  
  100.   (** builds the AST from a char stream *)
  101.   let parse stream =
  102.     let tokens = tokenize stream in
  103.     build_ast tokens
  104. end
  105.  
  106. module Optimizer = struct
  107.   open ParseTree
  108.  
  109.   (** groups moves & adds *)
  110.   let rec group = function
  111.     | Move a :: Move b :: rest ->
  112.       let lst = if a + b <> 0 then Move (a + b) :: rest else rest in  
  113.       group lst
  114.     | Add a :: Add b :: rest ->
  115.       let lst = if a + b <> 0 then Add (a + b) :: rest else rest in  
  116.       group lst
  117.     | Loop a :: rest -> (Loop (group a)) :: (group rest)
  118.     | other :: rest -> other :: (group rest)
  119.     | [] -> []
  120.  
  121.   (** replace known loops with faster operations *)
  122.   let rec unroll ast =
  123.     let replace = function
  124.       | [Add (-1)] -> Reset
  125.       | [Move a; Add n; Move b; Add (-1)]
  126.         when a = -b -> AddMultToCell (n, a)
  127.       | [Move a; Add n1; Move b; Add n2; Move c; Add (-1)]
  128.         when a + b = -c && n1 = n2 -> AddMultToCell2 (n1, a, a+b)          
  129.       | other -> Loop (unroll other)
  130.     in
  131.     match ast with
  132.     | Loop ops :: rest -> replace ops :: unroll rest  
  133.     | other :: rest -> other :: (unroll rest)
  134.     | [] -> []
  135.  
  136.   (** replace move, add and revert back to a distant add *)
  137.   let in_place_adds ast =
  138.     let rec loop = function
  139.       | Move i :: Add n :: Move j :: rest
  140.         when i = -j -> AddTo (n, i) :: loop rest
  141.       | Loop ops :: rest -> Loop (loop ops) :: loop rest  
  142.       | other :: rest -> other :: loop rest
  143.       | [] -> []
  144.     in loop ast
  145.  
  146.   (** some mult are copies, don't need to reset *)
  147.   let replace_moves_with_copy ast =
  148.     let rec loop = function
  149.       | AddMultToCell2(n1, i, j) :: Move(k) :: AddMultToCell(n2, l) :: rest
  150.         when n1 = n2 && j = k && j = -l -> CopyMultToCell (n1, i) :: Move j :: loop rest
  151.       | Loop ops :: rest -> Loop (loop ops) :: loop rest  
  152.       | other :: rest -> other :: loop rest
  153.       | [] -> []
  154.     in loop ast
  155.    
  156.    (** more unrolling *)    
  157.    let rec shortcut_loops ast =
  158.     let replace = function
  159.         (* we need to keep the loop in case the current cell is already 0 ! *)
  160.       | [Move a; Reset; Move b; Add (-1)]
  161.         when a = -b -> Loop [Move a; Reset; Move b; Reset]
  162.       | other -> Loop (shortcut_loops other)
  163.     in
  164.     match ast with
  165.     | Loop ops :: rest -> replace ops :: shortcut_loops rest  
  166.     | other :: rest -> other :: (shortcut_loops rest)
  167.     | [] -> []
  168.  
  169.   let (<<) f1 f2 = fun x -> f1 (f2 x)
  170.  
  171.   let optimize =
  172.     let first_pass = replace_moves_with_copy << in_place_adds << unroll << group
  173.     and second_pass = group << shortcut_loops
  174.     in second_pass << first_pass
  175.  
  176. end
  177.  
  178. (** Runs the AST *)
  179. module Interpreter = struct
  180.   open ParseTree
  181.  
  182.   let memory = Array.make 30_000 0
  183.   let pointer = ref 0
  184.  
  185.   let rec exec ast =
  186.     let exec_node = function
  187.       | Move i -> pointer := !pointer + i
  188.       | Add i -> memory.(!pointer) <- memory.(!pointer) + i
  189.       | Output -> Printf.printf "%c%!" (char_of_int memory.(!pointer))
  190.       | Input ->
  191.           let c = input_char stdin in
  192.           memory.(!pointer) <- int_of_char c
  193.       | Loop nodes ->
  194.           while memory.(!pointer) <> 0 do
  195.             exec nodes;
  196.           done
  197.       (* optimizations *)
  198.       | Reset -> memory.(!pointer) <- 0
  199.       | AddMultToCell (n, i) ->
  200.         memory.(!pointer + i) <- memory.(!pointer + i) + (memory.(!pointer)*n);
  201.         memory.(!pointer) <- 0
  202.       | AddMultToCell2 (n, i, j) ->
  203.         memory.(!pointer + i) <- memory.(!pointer + i) + (memory.(!pointer)*n);
  204.         memory.(!pointer + j) <- memory.(!pointer + j) + (memory.(!pointer)*n);
  205.         memory.(!pointer) <- 0
  206.       | CopyMultToCell (n, i) ->
  207.         memory.(!pointer + i) <- memory.(!pointer + i) + (memory.(!pointer)*n)
  208.       | AddTo (n, i) -> memory.(!pointer + i) <- memory.(!pointer + i) + n
  209.     in
  210.     List.iter exec_node ast
  211. end
  212.  
  213. let brainfuck optimize dump filename =
  214.   let stream = Stream.of_channel (open_in filename) in
  215.   let ast = Parser.parse stream in
  216.   let ast' = if !optimize then Optimizer.optimize ast else ast in
  217.   if !dump then ParseTree.dump ast' else Interpreter.exec ast'
  218.  
  219. let _ =
  220.   let dump = ref false in
  221.   let optimize = ref false in
  222.   let args = [
  223.     ("-optimize", Arg.Set optimize, "optimize ast");
  224.     ("-dump", Arg.Set dump, "dump ast, don't execute");
  225.     ] in
  226.   Arg.parse args (brainfuck optimize dump) "usage"
Advertisement
Add Comment
Please, Sign In to add comment