PageRenderTime 58ms CodeModel.GetById 31ms RepoModel.GetById 0ms app.codeStats 0ms

/detail/common/error.sml

https://bitbucket.org/simona/mltk-specialize
Standard ML (SML) | 223 lines | 163 code | 35 blank | 25 comment | 14 complexity | c47b27ef42f8c92c0c3b294c4f916fd4 MD5 | raw file
  1. (* error.sml
  2. *
  3. * COPYRIGHT (c) 2007 The Manticore Project (http://manticore.cs.uchicago.edu)
  4. * All rights reserved.
  5. *
  6. * Common infrastructure for error reporting in the Manticore compiler.
  7. *)
  8. structure Error :> sig
  9. (* logical positions in the input stream *)
  10. type pos = AntlrStreamPos.pos
  11. type span = AntlrStreamPos.span
  12. type err_stream
  13. (* make an error stream. *)
  14. val mkErrStream : string -> err_stream
  15. val anyErrors : err_stream -> bool
  16. val sourceFile : err_stream -> string
  17. val sourceMap : err_stream -> AntlrStreamPos.sourcemap
  18. (* add error messages to the error stream *)
  19. val error : err_stream * string list -> unit
  20. val errorAt : err_stream * span * string list -> unit
  21. (* add warning messages to the error stream *)
  22. val warning : err_stream * string list -> unit
  23. val warningAt : err_stream * span * string list -> unit
  24. (* add an ml-antlr parse error to the error stream *)
  25. val parseError : ('tok -> string)
  26. -> err_stream
  27. -> (pos * 'tok AntlrRepair.repair_action)
  28. -> unit
  29. (* print the errors to an output stream *)
  30. val report : TextIO.outstream * err_stream -> unit
  31. (* source-code locations *)
  32. datatype location
  33. = UNKNOWN
  34. | LOC of {file : string, l1 : int, c1 : int, l2 : int, c2 : int}
  35. val location : err_stream * span -> location
  36. val position : err_stream * pos -> location
  37. val locToString : location -> string
  38. (* a term marked with a source-map span *)
  39. type 'a mark = {span : span, tree : 'a}
  40. end = struct
  41. structure SP = AntlrStreamPos
  42. structure Repair = AntlrRepair
  43. structure F = Format
  44. type pos = SP.pos
  45. type span = SP.span
  46. datatype severity = WARN | ERR
  47. type error = {
  48. kind : severity,
  49. pos : span option,
  50. msg : string
  51. }
  52. (* an error stream collects the errors and warnings generated for
  53. * a compilation unit.
  54. *)
  55. datatype err_stream = ES of {
  56. srcFile : string,
  57. sm : SP.sourcemap, (* the source map for mapping positions to *)
  58. (* source-file positions *)
  59. errors : error list ref,
  60. numErrors : int ref,
  61. numWarnings : int ref
  62. }
  63. (* make an error stream. *)
  64. fun mkErrStream filename = ES{
  65. srcFile = filename,
  66. sm = SP.mkSourcemap' filename,
  67. errors = ref [],
  68. numErrors = ref 0,
  69. numWarnings = ref 0
  70. }
  71. fun anyErrors (ES{numErrors, ...}) = (!numErrors > 0)
  72. fun sourceFile (ES{srcFile, ...}) = srcFile
  73. fun sourceMap (ES{sm, ...}) = sm
  74. fun addErr (ES{errors, numErrors, ...}, pos, msg) = (
  75. numErrors := !numErrors + 1;
  76. errors := {kind=ERR, pos=pos, msg=msg} :: !errors)
  77. fun addWarn (ES{errors, numWarnings, ...}, pos, msg) = (
  78. numWarnings := !numWarnings + 1;
  79. errors := {kind=WARN, pos=pos, msg=msg} :: !errors)
  80. fun parseError tok2str es (pos, repair) = let
  81. val toksToStr = (String.concatWith " ") o (List.map tok2str)
  82. val msg = (case repair
  83. of Repair.Insert toks => ["syntax error; try inserting \"", toksToStr toks, "\""]
  84. | Repair.Delete toks => ["syntax error; try deleting \"", toksToStr toks, "\""]
  85. | Repair.Subst{old, new} => [
  86. "syntax error; try substituting \"", toksToStr new, "\" for \"",
  87. toksToStr old, "\""
  88. ]
  89. | Repair.FailureAt tok => ["syntax error at ", tok2str tok]
  90. (* end case *))
  91. in
  92. addErr (es, SOME(pos, pos), String.concat msg)
  93. end
  94. (* add error messages to the error stream *)
  95. fun error (es, msg) = addErr (es, NONE, String.concat msg)
  96. fun errorAt (es, span, msg) = addErr (es, SOME span, String.concat msg)
  97. (* add warning messages to the error stream *)
  98. fun warning (es, msg) = addWarn (es, NONE, String.concat msg)
  99. fun warningAt (es, span, msg) = addWarn (es, SOME span, String.concat msg)
  100. (* sort a list of errors by position in the source file *)
  101. val sort = let
  102. fun lt (NONE, NONE) = false
  103. | lt (NONE, _) = true
  104. | lt (_, NONE) = false
  105. | lt (SOME(l1, r1), SOME(l2, r2)) = (case Position.compare(l1, l2)
  106. of LESS => true
  107. | EQUAL => (Position.compare(r1, r2) = LESS)
  108. | GREATER => false
  109. (* end case *))
  110. fun cmp (e1 : error, e2 : error) = lt(#pos e1, #pos e2)
  111. in
  112. ListMergeSort.sort cmp
  113. end
  114. (* source-code locations *)
  115. datatype location
  116. = UNKNOWN
  117. | LOC of {file : string, l1 : int, c1 : int, l2 : int, c2 : int}
  118. fun location (ES{sm, ...}, (p1, p2) : span) =
  119. if (p1 = p2)
  120. then let
  121. val {fileName=SOME f, lineNo, colNo} = SP.sourceLoc sm p1
  122. in
  123. LOC{file=f, l1=lineNo, c1=colNo, l2=lineNo, c2=colNo}
  124. end
  125. else let
  126. val {fileName=SOME f1, lineNo=l1, colNo=c1} = SP.sourceLoc sm p1
  127. val {fileName=SOME f2, lineNo=l2, colNo=c2} = SP.sourceLoc sm p2
  128. in
  129. if (f1 <> f2)
  130. then LOC{file=f1, l1=l1, c1=c1, l2=l1, c2=c1}
  131. else LOC{file=f1, l1=l1, c1=c1, l2=l2, c2=c2}
  132. end
  133. fun position (ES{sm, ...}, p : pos) = let
  134. val {fileName=SOME f, lineNo, colNo} = SP.sourceLoc sm p
  135. in
  136. LOC{file=f, l1=lineNo, c1=colNo, l2=lineNo, c2=colNo}
  137. end
  138. fun locToString UNKNOWN = "<unknown>"
  139. | locToString (LOC{file, l1, l2, c1, c2}) =
  140. if (l1 = l2)
  141. then if (c1 = c2)
  142. then F.format "[%s:%d.%d] " [F.STR file, F.INT l1, F.INT c1]
  143. else F.format "[%s:%d.%d-%d] " [F.STR file, F.INT l1, F.INT c1, F.INT c2]
  144. else F.format "[%s:%d.%d-%d.%d] " [
  145. F.STR file, F.INT l1, F.INT c1, F.INT l2, F.INT c2
  146. ]
  147. fun printError (outStrm, ES{sm, ...}) = let
  148. fun pr {kind, pos, msg} = let
  149. val kind = (case kind of ERR => "Error" | Warn => "Warning")
  150. val pos = (case pos
  151. of SOME(p1, p2) => if (p1 = p2)
  152. then let
  153. val {fileName=SOME f, lineNo, colNo} = SP.sourceLoc sm p1
  154. in
  155. F.format "[%s:%d.%d] " [
  156. F.STR f, F.INT lineNo, F.INT colNo
  157. ]
  158. end
  159. else let
  160. val {fileName=SOME f1, lineNo=l1, colNo=c1} = SP.sourceLoc sm p1
  161. val {fileName=SOME f2, lineNo=l2, colNo=c2} = SP.sourceLoc sm p2
  162. in
  163. if (f1 <> f2)
  164. then F.format "[%s:%d.%d-%s:%d.%d] " [
  165. F.STR f1, F.INT l1, F.INT c1,
  166. F.STR f2, F.INT l2, F.INT c2
  167. ]
  168. else if (l1 <> l2)
  169. then F.format "[%s:%d.%d-%d.%d] " [
  170. F.STR f1, F.INT l1, F.INT c1,
  171. F.INT l2, F.INT c2
  172. ]
  173. else F.format "[%s:%d.%d-%d] " [
  174. F.STR f1, F.INT l1, F.INT c1, F.INT c2
  175. ]
  176. end
  177. (* end case *))
  178. in
  179. TextIO.output (outStrm, String.concat [pos, kind, ": ", msg, "\n"])
  180. end
  181. in
  182. pr
  183. end
  184. fun report (outStrm, es as ES{errors, ...}) =
  185. List.app (printError (outStrm, es)) (sort (!errors))
  186. (* a term marked with a source-map span *)
  187. type 'a mark = {span : span, tree : 'a}
  188. end