F# Interactiveはとても便利なのですが、セミコロンを2つ入力するのが面倒です。そこでセミコロンを省略して、インデントも自動化してみました。使用例は以下の通りです。空行でインデントから抜けています。
> let f x= - if x%2=0 then - "偶数" - - else - "奇数" - - val f : int -> string > f 23 val it : string = "奇数" > f 44 val it : string = "偶数"
console.fsを作り直しましたので、丸ごと差し替えます。
#light namespace Microsoft.FSharp.Compiler.Interactive open System open System.IO open System.Text open System.Collections.Generic type ReadLineConsole(complete: (string option * string -> seq<string>)) as this = let history = new List<string>() let log = Path.Combine(Directory.GetCurrentDirectory(), "log.fsx") let mutable complete = complete do if File.Exists(log) then this.AppendLog("") this.AppendLog("// " + DateTime.Now.ToString()) member x.Complete with set(v) = (complete <- v) member x.Prompt = "> " member x.ReadLine() = let count(s : string, ch : char) = let mutable ret = 0 for c in s do if c = ch then ret <- ret + 1 ret let getWords(s : string) = let ret = new List<string>() let rec getWords(p1, p2) = if p2 >= s.Length || not (Char.IsLetterOrDigit(s.[p2])) then if p1 < p2 then ret.Add(s.Substring(p1, p2 - p1)) if p2 < s.Length then getWords(p2 + 1, p2 + 1) else getWords(p1, p2 + 1) getWords(0, 0) ret let lines = ref 0 let p1 = ref 0 let p2 = ref 0 let sb = new StringBuilder() let rec readLine(indent : string) = if indent <> "" || !lines > 0 then Console.Write(indent + "- ") let line = Console.ReadLine().Trim() if indent = "" && line = "#h" then for cmd in history do Console.WriteLine(cmd) Console.Write(x.Prompt) readLine(indent) else if String.IsNullOrEmpty(line) then () else if sb.Length > 0 then sb.AppendLine() |> ignore sb.Append(indent + line) |> ignore lines := !lines + 1 if line.StartsWith("//") then readLine(indent) else let d1 = count(line, '(') - count(line, ')') let d2 = count(line, '[') - count(line, ']') p1 := !p1 + d1 p2 := !p2 + d2 let words = getWords(line) let w1 = if words.Count > 0 then words.[words.Count - 1] else "" if (!p1 <> 0 && d1 <> 0) || (!p2 <> 0 && d2 <> 0) || line.EndsWith("=") || line.EndsWith("->") || w1 = "in" || w1 = "do" || w1 = "then" || w1 = "else" then readLine(indent + " ") let w2 = if words.Count > 0 then words.[0] else "" if !p1 <> 0 || !p2 <> 0 || indent <> "" || (line.EndsWith(";") && not(line.EndsWith(";;"))) || (w2 = "if" && not(words.Contains("then") && words.Contains("else"))) || w2 = "match" then readLine(indent) readLine("") let ret = sb.ToString() history.Add(ret) x.AppendLog(ret) if ret.EndsWith(";;") then ret else if !lines = 1 then ret + ";;" else ret + Environment.NewLine + ";;" member x.AppendLog(cmd : string) = use fs = new FileStream(log, FileMode.Append) use sw = new StreamWriter(fs) sw.WriteLine(cmd)
履歴管理はコマンドプロンプトの機能に任せているため、自動的に日本語にも対応しています。ASTを解析しているわけではないため、インデントの判断はかなりやっつけです。必要な部分でインデントされなかったり勝手に抜けたりすると思われるので、その都度修正しながら使うことになるでしょう。
自動的にlog.fsxというファイルにログを取ります。対話的に試行錯誤してから、最終的にソースとしてまとめるような使い方を想定しています。