summaryrefslogtreecommitdiff
path: root/miralib/ex/box.m
diff options
context:
space:
mode:
Diffstat (limited to 'miralib/ex/box.m')
-rwxr-xr-xmiralib/ex/box.m244
1 files changed, 244 insertions, 0 deletions
diff --git a/miralib/ex/box.m b/miralib/ex/box.m
new file mode 100755
index 0000000..87d404c
--- /dev/null
+++ b/miralib/ex/box.m
@@ -0,0 +1,244 @@
+#! /home/dat/mira/states/src/miranda/mira -exec
+|| Contributed by John Cupitt, University of Kent
+
+||A while ago Steve Hill (I think) appealed for useful Miranda programs
+||-- well, here is the greatest productivity aid for Miranda hackers
+||since ball-point pens. A 'vi' type filter to *rebox your comments*!!
+||Amazing. It turns dull, unexciting notes like:
+
+|| Given a node in the tree,
+||examine the branches and chose the exit with the
+|| highest score.
+
+||into:
+
+||----------------------------------------------------------------------||
+|| Given a node in the tree, examine the branches and chose the exit ||
+|| with the highest score. ||
+||----------------------------------------------------------------------||
+
+||Any comments welcome -- my Miranda is not as hot as it could be ...
+
+||John
+
+||----------------------------------------------------------------------||
+|| Box up Miranda comments. A filter from stdin to stdout
+|| do_box :: [char] -> [char] ||
+|| - Strip ||s, reformat, rebox. ||
+||----------------------------------------------------------------------||
+
+main = [Stdout (do_box $-)]
+
+||----------------------------------------------------------------------||
+|| Reboxing done in a pipeline of five stages. ||
+|| - Split the input into lines ||
+|| - Strip '||'s from input ||
+|| - Lex the input, breaking into tokens ||
+|| - Rejig tokens to produce fmted type output ||
+|| - Output tokens as [char] with a box drawn around them ||
+|| Formatting rules: ||
+|| - Lines starting '||-' are deleted ||
+|| - Leading & trailing '||' removed ||
+|| - Lines starting with a tab are not reformatted ||
+|| - Blank lines are 'new paragraph' ||
+||----------------------------------------------------------------------||
+
+||----------------------------------------------------------------------||
+|| First a few types and useful little functions. ||
+||----------------------------------------------------------------------||
+
+|| Useful constants
+outWid = 68 || Width of the text in our boxes
+boxWid = 72 || Size of the box we draw
+
+|| A token
+tok ::= Word [char] | Newpara | Line [char]
+
+|| Useful character classifier
+whitespace :: char -> bool
+whitespace ch
+ = True, if ch = '\n' \/ ch = '\t' \/ ch = ' '
+ = False, otherwise
+
+|| An edge of a box boxWid across
+edge :: [char]
+edge = "||" ++ (rep (boxWid-2) '-') ++ "||\n"
+
+|| Find the length of a line containing tabs
+len :: [char] -> num
+len str
+ = len' 0 str
+ where
+ len' n []
+ = n
+ len' n (a:rest)
+ = len' (n+tab_space) rest, if a = '\t'
+ = len' (n+1) rest, otherwise
+ where
+ tab_space
+ = 8 - (n mod 8)
+
+|| Useful when doing output --- only attach first param if its not [].
+no_blank :: [char] -> [[char]] -> [[char]]
+no_blank a b
+ = a : b, if a ~= []
+ = b, otherwise
+
+||----------------------------------------------------------------------||
+|| The main function. Call from a shell script in your /bin directory ||
+|| looking something like: ||
+|| #! /usr/bin/mira -exec ||
+|| main = [Stdout (do_box $-)] ||
+|| %include "../mira/box/box.m" ||
+||----------------------------------------------------------------------||
+
+do_box :: [char] -> [char]
+do_box input
+ = edge ++ rejig input ++ edge
+ where
+ rejig = re_box . format . lex_start . strip_start . split
+
+||----------------------------------------------------------------------||
+|| The first stage in processing. Split the input [char] into lines as ||
+|| [[char]]. ||
+||----------------------------------------------------------------------||
+
+|| Split the text into a list of lines
+split :: [char] -> [[char]]
+split input
+ = split' [] input
+ where
+ split' sofar (a:input)
+ = sofar : split input, if a = '\n'
+ = split' (sofar ++ [a]) input, otherwise
+ split' sofar []
+ = no_blank sofar [] || No extra blank lines!
+
+||----------------------------------------------------------------------||
+|| The next stage ... strip old '||'s from the input. Remove: ||
+|| - Lines starting '||-' ||
+|| - Strip leading '||'s ||
+|| - Strip trailing '||'s & trailing spaces ||
+||----------------------------------------------------------------------||
+
+|| At the start of a line:
+strip_start :: [[char]] -> [[char]]
+strip_start ([]:input)
+ = [] : strip_start input || Keep blank lines
+strip_start (('|':'|':line):input)
+ = strip_start' line
+ where
+ strip_start' ('-':rest)
+ = strip_start input || Strip '||---||' lines
+ strip_start' rest
+ = strip_rest rest input || Strip leading '||'
+strip_start (line:input)
+ = strip_rest line input || Pass rest through
+strip_start []
+ = []
+
+|| Scan along the rest of the line looking for trailing '||'s to strip.
+strip_rest :: [char] -> [[char]] -> [[char]]
+strip_rest line input
+ = strip_rest' (rev line) input
+ where
+ strip_rest' ('|':'|':rest) input
+ = strip_rest' rest input || Strip trailing ||
+ strip_rest' (x:rest) input
+ = strip_rest' rest input, if whitespace x
+ strip_rest' line input
+ = (rev line) : strip_start input
+
+|| Efficient(ish) reverse
+rev list
+ = rev' [] list
+ where
+ rev' sofar (a:x)
+ = rev' (a:sofar) x
+ rev' sofar []
+ = sofar
+
+||----------------------------------------------------------------------||
+|| The next stage ... Break the input into Word, Newpara and Line ||
+|| tokens. Newpara for blank lines and line starting with space; Line ||
+|| for lines starting with a tab. ||
+||----------------------------------------------------------------------||
+
+|| At the start of a line.
+lex_start :: [[char]] -> [tok]
+lex_start ([]:input)
+ = Newpara : lex_start input || Preserve blank lines
+lex_start (('\t':rest):input)
+ = Line ('\t':rest) : lex_start input || Don't format tab lines
+lex_start (line:input)
+ = lex_rest (strip_ws line) input || Lex to eol
+lex_start []
+ = []
+
+|| In the middle of a line. Try to take words off the front of what we
+|| have so far.
+lex_rest :: [char] -> [[char]] -> [tok]
+lex_rest [] input
+ = lex_start input
+lex_rest sofar input
+ = Word wd : lex_rest (strip_ws rest) input
+ where
+ (wd, rest)
+ = break_word sofar
+
+|| Strip ws from the start of the line
+strip_ws (a:input)
+ = (a:input), if ~whitespace a
+ = strip_ws input, otherwise
+strip_ws []
+ = []
+
+|| Break the word from the front of a line of text. Return the remains
+|| of the line along with the word.
+break_word :: [char] -> ([char], [char])
+break_word (a:line)
+ = ([a] ++ rest, tag), if ~whitespace a
+ = ([], (a:line)), otherwise
+ where
+ (rest, tag)
+ = break_word line
+break_word []
+ = ([],[])
+
+||----------------------------------------------------------------------||
+|| Almost the last stage ... Turn [tok] back into [[char]]. Format ||
+|| onto outWid character lines. ||
+||----------------------------------------------------------------------||
+
+format :: [tok] -> [[char]]
+format input
+ = format' [] input
+ where
+ format' sofar (Word wd:rest)
+ = format' (sofar ++ " " ++ wd) rest, if #sofar + #wd < outWid
+ = sofar : format' (" " ++ wd) rest, otherwise
+ format' sofar (Newpara:rest)
+ = no_blank sofar ([] : format rest)
+ format' sofar (Line line:rest)
+ = no_blank sofar (line : format rest)
+ format' sofar []
+ = no_blank sofar []
+
+||----------------------------------------------------------------------||
+|| The final stage. Box up a list of formatted lines. Try to be clever ||
+|| about using tabs on the ends of lines. ||
+||----------------------------------------------------------------------||
+
+|| Draw a box boxWid across.
+re_box :: [[char]] -> [char]
+re_box (line:rest)
+ = "||" ++ line ++ padding ++ "||\n" ++ (re_box rest)
+ where
+ padding
+ = rep n_tab '\t'
+ n_tab
+ = (boxWid - line_length + 7) div 8
+ line_length
+ = len ("||" ++ line)
+re_box []
+ =[]