PageRenderTime 53ms CodeModel.GetById 15ms RepoModel.GetById 1ms app.codeStats 0ms

/FParsec/Error.fs

https://bitbucket.org/banshee/fparsec-samples
F# | 262 lines | 216 code | 37 blank | 9 comment | 56 complexity | 272a9231e298fe8e52687e4354ed233b MD5 | raw file
Possible License(s): BSD-2-Clause
  1. // Copyright (c) Stephan Tolksdorf 2008-2009
  2. // License: BSD-style. See accompanying documentation.
  3. module FParsec.Error
  4. open System.Diagnostics
  5. open FParsec.Internals
  6. #nowarn "61" // "The containing type can use 'null' as a representation value for its nullary union case. This member will be compiled as a static member."
  7. let invalidPos = Pos("invalid position", -1L, -1L, -1L)
  8. [<DebuggerDisplay("{GetDebuggerDisplay(),nq}")>]
  9. type ErrorMessage =
  10. | Expected of string
  11. | Unexpected of string
  12. | Message of string
  13. | CompoundError of string * Pos * ErrorMessageList
  14. | BacktrackPoint of Pos * ErrorMessageList
  15. | OtherError of obj
  16. with
  17. // the default DebuggerDisplay generated by the F# compiler doesn't use the DebuggerDisplay for ErrorMessageList
  18. member internal t.GetDebuggerDisplay() =
  19. match t with
  20. | Expected(str) -> "Expected \"" + str + "\""
  21. | Unexpected(str) -> "Unexpected \"" + str + "\""
  22. | Message(str) -> "Message \"" + str + "\""
  23. | OtherError o -> "OtherError(" + o.ToString() + ")"
  24. | CompoundError(str, pos, error)
  25. -> "CompoundError(\"" + str + "\", " + pos.ToString() + ", " + ErrorMessageList.GetDebuggerDisplay(error) + ")"
  26. | BacktrackPoint(pos, error)
  27. -> "BacktrackPoint(" + pos.ToString() + ", " + ErrorMessageList.GetDebuggerDisplay(error) + ")"
  28. and [<CompilationRepresentation(CompilationRepresentationFlags.UseNullAsTrueValue);
  29. StructuralEquality(false); StructuralComparison(false)>]
  30. [<DebuggerTypeProxy(typeof<ErrorMessageListDebugView>);
  31. DebuggerDisplay("{ErrorMessageList.GetDebuggerDisplay(this),nq}")>]
  32. ErrorMessageList =
  33. | AddErrorMessage of ErrorMessage * ErrorMessageList
  34. | NoErrorMessages
  35. with
  36. // compiled as static member, so valid for t = null
  37. member t.ToSet() =
  38. let rec convert (set: Set<_>) xs =
  39. match xs with
  40. | NoErrorMessages -> set
  41. | AddErrorMessage(hd, tl) ->
  42. match hd with
  43. // filter out empty messages
  44. | Expected(s)
  45. | Unexpected(s)
  46. | Message(s)
  47. when isNullOrEmpty s
  48. -> convert set tl
  49. // filter out uncomparable OtherErrors
  50. | OtherError obj when not (obj :? System.IComparable)
  51. -> convert set tl
  52. | _ -> convert (set.Add(hd)) tl
  53. convert (Set.empty<ErrorMessage>) t
  54. static member OfSeq(msgs: seq<ErrorMessage>) =
  55. msgs |> Seq.fold (fun lst msg -> AddErrorMessage(msg, lst)) NoErrorMessages
  56. // compiled as instance member, but F#'s operator '=' will handle the null cases
  57. override t.Equals(value: obj) =
  58. referenceEquals (t :> obj) value
  59. || match value with
  60. | null -> false
  61. | :? ErrorMessageList as other -> t.ToSet() = other.ToSet()
  62. | _ -> false
  63. interface System.IComparable with
  64. // see http://research.microsoft.com/fsharp/manual/spec2.aspx#_Toc207785725
  65. member t.CompareTo(value: obj) = // t can't be null (i.e. NoErrorMessages)
  66. match value with
  67. | null -> 1
  68. | :? ErrorMessageList as msgs -> compare (t.ToSet()) (msgs.ToSet())
  69. | _ -> invalidArg "value" "Object must be of type ErrorMessageList."
  70. override t.GetHashCode() = t.ToSet().GetHashCode()
  71. static member internal GetDebuggerDisplay(msgs: ErrorMessageList) =
  72. match msgs with
  73. | NoErrorMessages -> "NoErrorMessages"
  74. | _ -> match List.of_seq (Seq.truncate 3 (msgs.ToSet())) with
  75. | [] -> "NoErrorMessages"
  76. | [e1] -> "[" + e1.GetDebuggerDisplay() + "]"
  77. | [e1; e2] -> "[" + e1.GetDebuggerDisplay() + "; " + e2.GetDebuggerDisplay() + "; ...]"
  78. | e1::e2::tl -> "[" + e1.GetDebuggerDisplay() + "; " + e2.GetDebuggerDisplay() + "]"
  79. and [<Sealed>]
  80. ErrorMessageListDebugView(msgs: ErrorMessageList) =
  81. [<DebuggerBrowsable(DebuggerBrowsableState.RootHidden)>]
  82. member t.Items = msgs.ToSet() |> Set.to_array
  83. let expectedError label = AddErrorMessage(Expected(label), NoErrorMessages)
  84. let unexpectedError label = AddErrorMessage(Unexpected(label), NoErrorMessages)
  85. let messageError msg = AddErrorMessage(Message(msg), NoErrorMessages)
  86. let otherError obj = AddErrorMessage(OtherError(obj: obj), NoErrorMessages)
  87. let backtrackError (state: State<'u>) error =
  88. match error with
  89. | AddErrorMessage(BacktrackPoint _, NoErrorMessages) -> error
  90. | _ -> AddErrorMessage(BacktrackPoint(state.Pos, error), NoErrorMessages)
  91. let compoundError label (state: State<'u>) error =
  92. match error with
  93. | AddErrorMessage(BacktrackPoint(pos2, error2), NoErrorMessages)
  94. -> AddErrorMessage(CompoundError(label, pos2, error2), NoErrorMessages)
  95. | _ -> AddErrorMessage(CompoundError(label, state.Pos, error), NoErrorMessages)
  96. let rec concatErrorMessages msgs msgs2 =
  97. match msgs2 with
  98. | AddErrorMessage(hd, tl) -> concatErrorMessages (AddErrorMessage(hd, msgs)) tl
  99. | NoErrorMessages -> msgs
  100. let inline mergeErrors msgs1 msgs2 =
  101. match msgs1 with
  102. | NoErrorMessages -> msgs2
  103. | _ -> concatErrorMessages msgs1 msgs2
  104. let inline mergeErrorsIfNeeded (oldState: State<'u>) oldError (newState: State<'u>) newError =
  105. if isNull oldError || newState != oldState then newError
  106. else concatErrorMessages oldError newError
  107. let inline mergeErrorsIfNeeded3 (veryOldState: State<'u>) veryOldError
  108. (oldState: State<'u>) oldError
  109. (newState: State<'u>) newError =
  110. let error = mergeErrorsIfNeeded veryOldState veryOldError oldState oldError
  111. mergeErrorsIfNeeded oldState error newState newError
  112. let printErrorLine (stream: CharStream) (index: int64) (tw: System.IO.TextWriter) (indent: string) (columnWidth: int) =
  113. let iter = stream.Seek(index)
  114. if index > iter.Index then
  115. invalidArg "index ""The given index lies beyond the end of the given CharStream."
  116. let space = columnWidth - indent.Length
  117. if space > 0 then
  118. let leftBound = max (index - int64 space) 0L
  119. let off = int32 (index - leftBound)
  120. let s = iter.Advance(-off).Read(2*space)
  121. let newlineChars = [|'\r'; '\n'; '\u0085'; '\u000C'; '\u2028'; '\u2029'|]
  122. let lineBegin = if off > 0 then s.LastIndexOfAny(newlineChars, off - 1) + 1 else 0
  123. let lineEnd = let i = s.IndexOfAny(newlineChars, lineBegin) in if i >= 0 then i else s.Length
  124. let space = if lineEnd > off then space else space - 1
  125. let left = max (min (lineEnd - space) (off - space/2)) lineBegin
  126. let right = min (max (lineBegin + space) (off + (space - space/2))) lineEnd
  127. if right > left then
  128. fprintfn tw "%s%s" indent (s.Substring(left, right - left).Replace('\t', ' '))
  129. fprintf tw "%s%s^" indent (new string(' ', off - left))
  130. if not iter.IsEndOfStream
  131. || columnWidth - (indent.Length + off - left + 1) < 14
  132. then tw.WriteLine()
  133. else tw.WriteLine("(end of input)")
  134. elif not iter.IsEndOfStream && columnWidth - indent.Length >= 23 then
  135. fprintfn tw "%sError on an empty line." indent
  136. elif iter.IsEndOfStream && columnWidth - indent.Length >= 22 then
  137. fprintfn tw "%sError at end of input." indent
  138. else
  139. tw.WriteLine(if columnWidth >= indent.Length then indent else "")
  140. else
  141. tw.WriteLine(if columnWidth = indent.Length then indent else "")
  142. /// the default position printer
  143. let internal printPosition (tw: System.IO.TextWriter) (p: Pos) (indent: string) (columnWidth: int) =
  144. fprintfn tw "%sError in %s%sLn: %i Col: %i"
  145. indent p.StreamName (if System.String.IsNullOrEmpty(p.StreamName) then "" else ": ") p.Line p.Column
  146. [<Sealed>]
  147. type ParserError(pos: Pos, error: ErrorMessageList) =
  148. do if isNull pos then nullArg "pos"
  149. member t.Pos = pos
  150. member T.Error = error
  151. override t.ToString() =
  152. use sw = new System.IO.StringWriter()
  153. t.WriteTo(sw)
  154. sw.ToString()
  155. member t.ToString(streamWhereErrorOccurred: CharStream) =
  156. use sw = new System.IO.StringWriter()
  157. t.WriteTo(sw, streamWhereErrorOccurred = streamWhereErrorOccurred)
  158. sw.ToString()
  159. member t.WriteTo(textWriter: System.IO.TextWriter,
  160. ?positionPrinter: System.IO.TextWriter -> Pos -> string -> int -> unit,
  161. ?columnWidth: int, ?initialIndention: string, ?indentionIncrement: string,
  162. ?streamWhereErrorOccurred: CharStream) =
  163. let tw = textWriter
  164. let positionPrinter = defaultArg positionPrinter printPosition
  165. let positionPrinter = match streamWhereErrorOccurred with
  166. | None -> positionPrinter
  167. | Some stream ->
  168. let originalStreamName = t.Pos.StreamName
  169. fun tw pos indent columnWidth ->
  170. positionPrinter tw pos indent columnWidth
  171. if pos.StreamName = originalStreamName then
  172. printErrorLine stream pos.Index tw indent columnWidth
  173. let columnWidth = defaultArg columnWidth 79
  174. let ind = defaultArg initialIndention ""
  175. let indIncrement = defaultArg indentionIncrement " "
  176. let rec printMessages (pos: Pos) (msgs: ErrorMessageList) ind =
  177. positionPrinter tw pos ind columnWidth
  178. let nra() = new ResizeArray<_>()
  179. let expectedA, unexpectedA, messageA, compoundA, backtrackA = nra(), nra(), nra(), nra(), nra()
  180. let mutable otherCount = 0
  181. for msg in msgs.ToSet() do // iterate over ordered unique messages
  182. match msg with
  183. | Expected s -> expectedA.Add(s)
  184. | Unexpected s -> unexpectedA.Add(s)
  185. | Message s -> messageA.Add(s)
  186. | OtherError obj -> otherCount <- otherCount + 1
  187. | CompoundError (s, pos2, msgs2) ->
  188. if not (System.String.IsNullOrEmpty(s)) then expectedA.Add(s)
  189. compoundA.Add((s, pos2, msgs2))
  190. | BacktrackPoint (pos2, msgs2) ->
  191. backtrackA.Add((pos2, msgs2))
  192. let printArray title (a: ResizeArray<string>) (sep: string) =
  193. fprintf tw "%s%s: " ind title
  194. let n = a.Count
  195. for i = 0 to n - 3 do
  196. fprintf tw "%s, " a.[i]
  197. if n > 1 then fprintf tw "%s%s" a.[n - 2] sep
  198. if n > 0 then fprintf tw "%s" a.[n - 1]
  199. fprintfn tw ""
  200. if expectedA.Count > 0 then
  201. printArray "Expecting" expectedA " or "
  202. if unexpectedA.Count > 0 then
  203. printArray "Unexpected" unexpectedA " and "
  204. if messageA.Count > 0 then
  205. let ind = if expectedA.Count > 0 || unexpectedA.Count > 0 then
  206. fprintfn tw "%sOther errors:" ind;
  207. ind + indIncrement
  208. else ind
  209. for m in messageA do
  210. fprintfn tw "%s%s" ind m
  211. for s, pos2, msgs2 in compoundA do
  212. fprintfn tw ""
  213. fprintfn tw "%s%s could not be parsed because:" ind s
  214. printMessages pos2 msgs2 (ind + indIncrement)
  215. for pos2, msgs2 in backtrackA do
  216. fprintfn tw ""
  217. fprintfn tw "%sThe parser backtracked after:" ind
  218. printMessages pos2 msgs2 (ind + indIncrement)
  219. if expectedA.Count = 0 && unexpectedA.Count = 0 && messageA.Count = 0
  220. && compoundA.Count = 0 && backtrackA.Count = 0
  221. then
  222. fprintfn tw "%sUnknown error(s)" ind
  223. printMessages pos error ind
  224. let _raiseInfiniteLoopException (name: string) (state: State<'u>) =
  225. failwith (concat4 (state.Pos.ToString()) ": The combinator '" name "' was applied to a parser that succeeds without changing the parser state. (If no exception had been raised, the combinator likely would have entered an infinite loop.)")