orbitz avatar orbitz committed 999ea31

Adding Adding support for various cases in -export, as well as some test files

Comments (0)

Files changed (3)

 	     ; msg         : string
 	     }
 
-type error_t =
-  | Forced_line_break
-  | Msg of string
 
 type warning = error
 
   | Top_level
   | Function
 
-type state = { program_options   : Program_options.t
+type state = { program_options       : Program_options.t
 	     (* The number of indentations, not the number of spaces *)
-	     ; indentation_level : int
+	     ; indentation_level     : int
+	     (*
+	      * The amount of spaces to indent, for alignments
+	      * this value includes whatever spaces are set by
+	      * indentation elevel
+	      * This is a list because we could have an arbitrary
+	      * level of alignment for recursive elements
+	      *)
+	     ; alignment_spaces      : int list
+	     ; alignment_spaces_curr : int
 	     (* This is the current line we are reading, starting at 0 *)
-	     ; src_line_number   : int
+	     ; src_line_number       : int
 	     (* This is current line number we are writing, starting at 0 *)
-	     ; dst_line_number   : int
-	     ; parser_state      : parser_state list
-	     ; line_builder      : Format_builder.t
-	     ; builder           : Format_builder.t
-	     ; warnings          : warning list
+	     ; dst_line_number       : int
+	     ; parser_state          : parser_state list
+	     ; line_builder          : Format_builder.t
+	     ; builder               : Format_builder.t
+	     ; warnings              : warning list
 	     }
 
+type error_t =
+  | Forced_line_break
+  | Msg of (state * string)
 
-let default_state program_options buffer =
-  { program_options   = program_options
-  ; indentation_level = 0
-  ; src_line_number   = 0
-  ; dst_line_number   = 0
-  ; parser_state      = [Top_level]
-  ; line_builder      = Format_builder.create ()
-  ; builder           = Format_builder.create ()
-  ; warnings          = []
+let default_state program_options =
+  { program_options       = program_options
+  ; indentation_level     = 0
+  ; alignment_spaces      = [0]
+  ; alignment_spaces_curr = 0
+  ; src_line_number       = 0
+  ; dst_line_number       = 0
+  ; parser_state          = [Top_level]
+  ; line_builder          = Format_builder.create ()
+  ; builder               = Format_builder.create ()
+  ; warnings              = []
   }
 
 
   | Token.Newline     -> "\n"
   | Token.Dot         -> "."
 
+let append_line state =
+  { state with
+    builder =
+      (Format_builder.append
+	 state.builder
+	 state.line_builder);
+    line_builder = Format_builder.create ()
+  }
+
 let incr ?(step = 1) num = num + step
 
 let incr_src_line state =
   { state with src_line_number = incr state.src_line_number }
 
 let incr_dst_line state =
+  let state = append_line state in
   { state with
     dst_line_number = incr state.dst_line_number;
     builder = Format_builder.add_token state.builder Newline
   }
 
+
+let (|>) d f = f d
+
+
 (*
  * Kind of lame I'm naming this bind but this is effectively
  * a monad-like thing so might as well stick to the literature?
 	    ; msg = msg
 	    }
 
+let error_of_error_t state = function
+  | Msg (err_state, msg) ->
+    fail err_state msg
+  | _ ->
+    fail state "Unknown failure"
+
 let add_warning state msg =
   { state with
     warnings = { line_number = state.dst_line_number
 	       }::state.warnings
   }
 
-let add_token state token =
+let add_token token state =
   { state with
     line_builder =
       Format_builder.add_token
 	token
   }
 
-let add_tokens state tokens =
+let add_tokens tokens state =
   { state with
     line_builder =
       Format_builder.add_tokens
   }
 
 
-let append_line state =
+let try_catch ~succ ~fail = function
+  | R.Success (state, rest) ->
+    succ state rest
+  | R.Failure failure ->
+    fail failure
+
+let try_catch_forced_line_break ~succ ~fail =
+  try_catch
+    ~succ:succ
+    ~fail:(function
+      | Forced_line_break ->
+	fail ()
+      | Msg (state, msg) ->
+	fail ())
+
+
+let line_length state =
+  (String.length
+     (Format_builder.to_string
+	state.line_builder
+	string_of_token))
+
+(*
+ * Sets the indentation level to whatever the
+ * current line length is
+ *)
+let push_alignment_spaces state =
   { state with
-    builder =
-      (Format_builder.append
-	 state.builder
-	 state.line_builder);
-    line_builder = Format_builder.create ()
+    alignment_spaces      = (line_length state
+			     - 1)::state.alignment_spaces;
+    alignment_spaces_curr = line_length state - 1
   }
 
+let pop_alignment_spaces state =
+  match state.alignment_spaces with
+    | [] ->
+      (*
+       * This is cheap, here because we can't
+       * express this invariant (AFAIK) at compile time
+       * in Ocaml and this *should never happen* which I
+       * think is what excpetions are for.  I think
+       *)
+      raise (Failure "No spaces left to pop")
+    | [_] ->
+      raise (Failure "Cannot pop base index")
+    | _::new_spaces::xs ->
+      {state with
+	alignment_spaces      = new_spaces::xs;
+	alignment_spaces_curr = new_spaces
+      }
+
+let add_alignment state =
+  add_space ~num:state.alignment_spaces_curr state
+
 let is_line_too_long state =
   (String.length
      (Format_builder.to_string
 let succeed state rest =
   R.Success (state, rest)
 
+
+(*
+ * These two functiosn exist for formatting lists of functions
+ * for things like -export
+ *)
+let rec format_func_list_oneline state = function
+    | (Keyword "]")::xs ->
+      let state =
+	add_token
+	  (Keyword "]")
+	  state
+      in
+      if is_line_too_long state then
+	R.Failure Forced_line_break
+      else
+	succeed
+	  state
+	  xs
+    | (Keyword ",")::xs ->
+      format_func_list_oneline
+	(state |> add_token (Keyword ",") |> add_space)
+	xs
+    | (Comment _)::_
+    | Newline::_      ->
+      R.Failure Forced_line_break
+    | x::xs ->
+      format_func_list_oneline
+	(add_token x state)
+	xs
+    | [] ->
+      R.Failure (Msg (state, "Unexpected end of input"))
+
+let rec format_func_list_multiline state code =
+  format_func_list_multiline_inside
+    (add_space state)
+    code
+and format_func_list_multiline_inside state = function
+  | (Keyword "]")::xs ->
+    succeed
+      (state
+	  |> incr_dst_line
+	  |> add_alignment
+	  |> add_token (Keyword "]")
+	  |> pop_alignment_spaces)
+      xs
+  | (Keyword ",")::(Comment com)::xs ->
+    (*
+     * We have a situation like:
+     * [ foo, % comment about foo
+     *   bar, % comment about bar
+     * ]
+     *
+     * Which we want to turn into
+     * [ foo % Comment about foo
+     * , bar % comment about bar
+     * ]
+     *)
+    format_func_list_multiline_inside
+      (state |> add_space |> add_token (Comment com))
+      (*
+       * Toss the ',' back on list
+       *)
+      ((Keyword ",")::xs)
+  | (Keyword ",")::xs ->
+    format_func_list_multiline_inside
+      (state
+	  |> incr_dst_line
+	  |> add_alignment
+	  |> add_token (Keyword ",")
+	  |> add_space)
+      xs
+  | Newline::(Comment com)::xs ->
+    format_func_list_multiline_inside
+      (state
+	  |> incr_dst_line
+	  |> incr_src_line
+	  |> add_alignment
+	  |> add_token (Comment com))
+      xs
+  | (Comment com)::xs ->
+    format_func_list_multiline_inside
+      (state
+	  |> add_space
+	  |> add_token (Comment com)
+	  |> add_alignment)
+      xs
+  | Newline::xs ->
+    format_func_list_multiline_inside
+      (state |> incr_src_line)
+      xs
+  | x::xs ->
+    format_func_list_multiline_inside
+      (state |> add_token x)
+      xs
+  | [] ->
+    R.Failure (Msg (state, "Unexpected end of input"))
+
+
+
 let rec format_top_level state = function
   | Newline::xs ->
-    let state' = append_line state in
-    format_top_level (incr_dst_line (incr_src_line state')) xs
+    format_top_level (incr_dst_line (incr_src_line state)) xs
   | (Comment text)::xs ->
     let state =
       add_token
+	(Comment text)
 	state
-	(Comment text)
     in
-    if is_line_too_long state then begin
+    if is_line_too_long state then
       format_top_level
 	(add_warning state "Comment longer than maximum line length")
 	xs
-    end
     else
       format_top_level state xs
   | (Keyword "-")::(Atom "module")::xs ->
       format_top_level
       (format_module
 	 (add_tokens
-	    state
-	    [Keyword "-"; Atom "module"])
+	    [Keyword "-"; Atom "module"]
+	    state)
 	 xs)
   | (Keyword "-")::(Atom "export")::xs ->
     bind
       format_top_level
       (format_export
 	 (add_tokens
-	    state
-	    [Keyword "-"; Atom "export"])
+	    [Keyword "-"; Atom "export"]
+	    state)
 	 xs)
   | [] ->
     succeed (append_line state) []
   | (Keyword "(")::(Atom mod_name)::(Keyword ")")::Dot::xs ->
     succeed
       (add_tokens
-	 state
-	 [Keyword "("; Atom mod_name; Keyword ")"; Dot])
+	 [Keyword "("; Atom mod_name; Keyword ")"; Dot]
+	 state)
       xs
   | _ ->
     fail state "Unknown -module declaration"
   | (Keyword "(")::(Keyword "[")::xs -> begin
     let state =
       add_tokens
+	[Keyword "("; Keyword "["]
 	state
-	[Keyword "("; Keyword "["]
     in
-    match format_list_oneline state xs with
-      | R.Success (state, rest) ->
+    try_catch
+      ~succ:(fun state rest ->
 	bind
 	  format_top_level
 	  (format_export_end
 	     state
-	     rest)
-      | R.Failure Forced_line_break ->
-	bind
-	  format_top_level
-	  (format_list_multiline
-	     state
-	     xs)
-      | R.Failure (Msg msg) ->
-	fail state msg
+	     rest))
+      ~fail:(fun _ ->
+	try_catch
+	  ~succ:(fun state rest ->
+	    bind
+	      format_top_level
+	      (format_export_end
+		 state
+		 rest))
+	  ~fail:(error_of_error_t state)
+	  (format_func_list_multiline
+	     (push_alignment_spaces state)
+	     xs))
+      (format_func_list_oneline
+	 state
+	 xs)
   end
   | _ ->
     fail state "Unknown -export declaration"
 and format_export_end state = function
   | (Keyword ")")::Dot::xs ->
     succeed
-      (add_tokens
-	 state
-	 [(Keyword ")"); Dot])
+      (state |> add_tokens [Keyword ")"; Dot])
       xs
   | _ ->
     fail state "Unexpected tokens in -export"
-and format_list_oneline state = function
-    | (Keyword "]")::xs ->
-      let state =
-	add_token
-	  state
-	  (Keyword "]")
-      in
-      if is_line_too_long state then
-	R.Failure Forced_line_break
-      else
-	succeed
-	  state
-	  xs
-    | (Keyword ",")::xs ->
-      format_list_oneline
-	(add_space
-	   (add_token
-	      state
-	      (Keyword ",")))
-	xs
-    | (Comment _)::_
-    | Newline::_ ->
-      R.Failure Forced_line_break
-    | x::xs ->
-      format_list_oneline
-	(add_token
-	   state
-	   x)
-	xs
-    | [] ->
-      R.Failure (Msg "Unexpected end of input")
-and format_list_multiline state = function
-    | _ -> fail state "Not implemented"
+
 
 let format_code program_options code =
   (*
    * Initiate the buffer at the number of tokens * 10, just
    * a reasonable round number
    *)
-  let buffer = Buffer.create (List.length code * 10) in
-  let state = default_state program_options buffer in
+  let state = default_state program_options in
   match format_top_level state code with
     | R.Success (state, []) ->
       let code =

test_files/test_export.erl

+% Single line
+-export([foo/1, bar/2]).
+
+% Multiple lines
+-export([foo/1, foo/2, foo/3, foo/4, foo/5, foo/6, foo/7, foo/8, foo/9, foo/10, foo/11]).
+
+% Forced newline
+-export([foo/1,
+         foo/2]).
+
+% With comment
+-export([foo/1, % Does things
+         foo/2]).
+
+% Identity
+-export([ foo/1 % Does things
+        , foo/2
+        ]).

test_files/test_module.erl

+-module(foo).
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.