summaryrefslogtreecommitdiff
path: root/retab.sml
blob: 8e2a054ec08570bfdccb311cdb085d8133b87a3e (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
(*
 *  retab (c) 2009 The Mana World development team
 *  License: GPL, version 2 or later
 *
 *  Compilation, e.g. (depends on SML implementation):
 *    mlton retab.sml
 *
 *  Example usage:
 *    tools/retab < db/mob_db.txt > db/mob_db.txt.new && mv db/mob_db.txt.new db/mob_db.txt
 *
 *  TODO:
 *    - Commas inside {} need to be seen as just one field when tabified
 *    - Commented lines should be left untabified
 *)

fun width (#"\t", i)	= let val m = i mod 8 in if m = 0 then 8 else m end
  | width (c, i)	= 1

fun encode_width (offset) (l) =
    let fun expand ([], i)	= []
	  | expand (c::tl, i)	= let val w = width (c, i)
				  in (c, i, i + w) :: expand (tl, i + w)
				  end
    in expand (l, offset)
    end

fun strip_blanks (#" "::tl)	= strip_blanks (tl)
  | strip_blanks (#"\t"::tl)	= strip_blanks (tl)
  | strip_blanks (#"\n"::tl)	= strip_blanks (tl)
  | strip_blanks (#"\r"::tl)	= strip_blanks (tl)
  | strip_blanks (other)	= other

fun clean (s) = rev (strip_blanks (rev (strip_blanks (s))))

fun split_comma (#","::tl)	= [#","] :: (split_comma (strip_blanks (tl)))
  | split_comma ([])		= []
  | split_comma (h::tl)		= (case split_comma (tl) of
				       []	=> [[h]]
				     | (h'::tl)	=> (h::h')::tl
				  )

fun expand (l : char list list list, offset : int) : char list list list =
    if List.all (null) (l) (* at the end of all rows *)
    then l
    else let fun splitlist ([]::tl)		= let val (heads, tails) = splitlist (tl)
						  in ([]::heads, []::tails)
						  end
	       | splitlist ((hrow::tlrow)::tl)	= let val (heads, tails) = splitlist (tl)
						  in (hrow::heads, tlrow::tails)
						  end
	       | splitlist ([])			= ([], [])
	     val (heads, tails) = splitlist (l)
	     val eheads = map (encode_width offset) heads

	     fun last_offset []		= offset
	       | last_offset (cell)	= let val (_, _, x)::_ = rev (cell) in x end
	     val desired_final_offset = foldl Int.max offset (map last_offset (eheads))
(*
	     val () = print ("start offset = " ^ Int.toString (offset) ^ "\n")
	     val () = print ("FINAL OFFSET = " ^ Int.toString (desired_final_offset) ^ "\n")
*)
	     fun align (i) = ((i + 7) div 8) * 8
	     val desired_final_offset = align(desired_final_offset)
(*
	     val () = print ("FINAL OFFSET (align) = " ^ Int.toString (desired_final_offset) ^ "\n")
*)
	     fun fill (offset) = if offset >= desired_final_offset
				 then []
				 else #"\t" :: fill (align (offset - 7) + 8)
	     val fill = if List.all null tails
			then fn _ => []
			else fill
	     fun tabexpand ([])		= fill (offset)
	       | tabexpand (cell)	= let val (_, _, finalwidth)::_ = rev (cell)
					      fun strip (x, _, _) = x
					  in rev (fill (finalwidth) @ rev (map strip (cell)))
					  end
	     val expanded_heads = map tabexpand (eheads)
	     val expanded_tails = expand (tails, desired_final_offset)
	 in ListPair.map op:: (expanded_heads, expanded_tails)
	 end

fun align_strings (s : string list) = map (implode o List.concat) (expand (map ((map clean) o split_comma o explode) s, 0))

(*val test = align_strings (["foo, bar, quux", "a,b", "0,01234567890,c"])*)

fun println (s) = (print (s);
		   print "\n")

fun read_input () =
    case TextIO.inputLine (TextIO.stdIn) of
	NONE	=> []
      | SOME s	=> s :: read_input ()

fun main (_, _) = app println (align_strings (read_input ()))

val () = main ("", [])